diff -Nru refind-0.12.0/BUILDING.txt refind-0.13.2/BUILDING.txt --- refind-0.12.0/BUILDING.txt 2020-02-13 03:28:13.000000000 +0000 +++ refind-0.13.2/BUILDING.txt 2021-02-27 04:01:39.000000000 +0000 @@ -1,3 +1,14 @@ +NOTE +==== + +The rEFInd Makefiles are complex, in order to support building in three ways +(described shortly). As a consequence (and, likely, because I'm not exactly +a Makefile guru), tracking of header files is basically non-existent. +Therefore, if you use git to update to a later version of rEFInd, you should +do a "make clean" before building a new version of rEFInd, to be sure any +changed data structures are cleanly applied to all the object files that use +them. + Requirements ============ @@ -23,10 +34,15 @@ "frozen" forms (UDK2014, UDK2017, UDK2018, and so on); however, the term "EDK2" is often used in reference to the TianoCore toolkit generally. To simplify matters, I officially support only one UDK version at any given - moment. Currently (rEFInd 0.11.3), this version is UDK2018; however, I - used UDK2017 for rEFInd 0.10.9 through 0.11.2, and UDK2014 unofficially - works via a different build procedure (see below), which is now - deprecated. + moment. Currently (rEFInd 0.12.1), this version is UDK2018; however, I + used UDK2017 for rEFInd 0.10.9 through 0.11.2, and UDK2014 may + unofficially work via a different build procedure (see below), which is + now deprecated. (This build process is broken with Ubuntu 20.04 and GCC + 9.3.) The UDK2018 version I use is available from + https://github.com/tianocore/tianocore.github.io/wiki/UDK2018. I tried a + newer version in February of 2021 (edk2-edk2-stable202011), but it + failed to compile because of the removal of in-tree libraries and other + problems. * The GNU-EFI package (http://sourceforge.net/projects/gnu-efi/). You can install this from a package called "gnu-efi"; however, rEFInd relies on @@ -39,18 +55,29 @@ a package manager. If you install from source code, you may need to adjust those Makefiles' paths. -Of the two toolkits, I prefer to use TianoCore because it produces binaries -that are about 5-30KiB smaller than those made by GNU-EFI, and I can easily -build 32-bit binaries on my 64-bit Linux installations. Also, I've had -problems on a 32-bit Mac Mini with the drivers produced by GNU-EFI hanging -the system. (I haven't encountered this problem on UEFI-based PCs.) That -said, the TianoCore EDK2 package is much harder to install, so you may -prefer to use GNU-EFI unless you have a specific need for the TianoCore -toolkit. Also, somewhere between GCC 5.4 and 7.3, problems have appeared -that prevent compiling the i386/IA32 binaries via TianoCore. Automated build -tools like the OpenSUSE Build Service (OBS) and the Ubuntu Personal Package -Archive (PPA) mechanism don't yet support TianoCore. - +Of the two toolkits, I recommend using GNU-EFI if you're compiling for your +native architecture (e.g., X64 on an X64/x86-64/AMD64 system). GNU-EFI is +*MUCH* easier to install, particularly if you're using an unsupported +version of TianoCore -- the TianoCore developers keep making changes that +break its ability to compile; and even the procedures I document here can +break on distributions other than what I use -- Ubuntu 20.04, currently. (I +suspect the TianoCore developers are trying to support too many build +platforms.). In the past, TianoCore-based rEFInd binaries supported some +features didn't work when building with GNU-EFI; but these GNU-EFI +limitations have long been in the past. Because GNU-EFI is a standard part +of most Linux distributions but TianoCore's EDK2 is not, binaries built for +most distributions and in archives like my own Ubuntu PPA for rEFInd are +built with GNU-EFI. TianoCore still has some advantages, though. One of +these is that cross-compiling with it is easier (if you can get the +TianoCore package to build). For this reason, I use it to build my binary +.zip files, since I can build the X64, IA32, and AARCH64 binaries on one +computer. Also, I've had problems on a 32-bit Mac Mini with the drivers +produced by GNU-EFI hanging the system. (I haven't encountered this problem +on UEFI-based PCs.) The TianoCore EDK2 is also usable on a wide variety of +OSes, including macOS and Windows, so you might be able to get rEFInd to +compile on these OSes using TianoCore, although I do not provide support for +such attempts. (The upcoming section, "Compiling rEFInd Under MacOS," does +provide some pointers, though.) Preparing Your Development Kit ============================== @@ -134,7 +161,7 @@ - TARGET_ARCH = X64 (on x86-64; leave this as IA32 on x86 or change it to AARCH64 on ARM64). If you plan to build multiple architectures, you can set this to "IA32 X64" or some other combination. - - TOOL_CHAIN_TAG = GCC49 (or other value depending on your GCC version; + - TOOL_CHAIN_TAG = GCC5 (or other value depending on your GCC version; type "gcc -v" to learn your GCC version number). Note that support for the latest GCC version takes a while to make it into the TianoCore toolkit, so if you're using a very recent GCC, you may need @@ -151,8 +178,8 @@ 6. The documentation refers to editing Conf/tools_def.txt in addition to Conf/target.txt, but doesn't specify what to change in - Conf/tools_def.txt. I haven't found it necessary to make any changes in - Conf/tools_def.txt EXCEPT for two cases: + Conf/tools_def.txt. Some of the changes I've needed to make in various + build environments include: * When using GCC 4.7 on a Fedora 17 system with the original UDK2014, GCC 4.7 was newer than the most recent GCC that TianoCore supported at @@ -172,11 +199,26 @@ *_GCC49_AARCH64_CC_PATH = /usr/bin/aarch64-linux-gnu-gcc Similar changes for other variables in the same block are necessary. -7. Type "make -C BaseTools/Source/C". (This step is not documented on the + * When compiling for x86 (IA32) on an x86-64 system running Ubuntu + 20.04, I needed to add "-fno-pie -fno-pic" to the GCC5_IA32_CC_FLAGS + definition. (At this time, TianoCore's UDK2018 and November 2020 + snapshots' GCC definitions maxed out at GCC5, but Ubuntu 20.04 shipped + with GCC 9.3.) + + * When compiling for ARM64 (AARCH64) on an x86-64 system running + Ubuntu 20.04, I needed to add "-fno-unwind-tables" to the + GCC5_AARCH64_CC_FLAGS definition. + +7. You may need to remove "-Werror" from two locations in + BaseTools/Source/C/Makefiles/header.makefile. I found this to be + necessary under Ubuntu 20.04 (using GCC 9.3), but not under Ubuntu 18.04 + (using GCC 7.5). + +8. Type "make -C BaseTools/Source/C". (This step is not documented on the EDK Web page.) Note that this requires the g++ compiler and UUID development libraries. -8. Type "build" to build the main set of EDK2 files. This process is +9. Type "build" to build the main set of EDK2 files. This process is likely to take a few minutes. This step requires Python 2; if you have Python 3 installed, you may need to adjust the default python for this build (for instance, by typing "eselect python set python2.7" in @@ -222,13 +264,12 @@ "gptsync_ia32.efi", "gptsync_x64.efi", or "gptsync_aa64.efi" program file, in the "gptsync" subdirectory. If you want to build IA32 binaries on an x86-64 (X64) system and if you're using TianoCore, type "ARCH=ia32 - make". (As noted earlier, this facility broke somewhere between GCC 5.4 - and GCC 7.3.) Similarly, you can specify "ARCH=aarch64" to cross-compile - for ARM64, but this works only if you're using the TianoCore build kit, - and only if you set up TianoCore for cross-compiling. If you plan to - build multiple architectures, be sure to copy the .efi file for the first - build out of the refind subdirectory and type "make clean" before - building the second architecture. + make". Similarly, you can specify "ARCH=aarch64" to cross-compile for + ARM64, but this works only if you're using the TianoCore build kit, and + only if you set up TianoCore for cross-compiling. If you plan to build + multiple architectures, be sure to copy the .efi file for the first build + out of the refind subdirectory and type "make clean" before building the + second architecture. The top-level rEFInd Makefile supports three toolchains, each of which provides several options to compile a subset of rEFInd's programs. (Note @@ -243,14 +284,16 @@ GNU-EFI toolkit. * all_gnuefi -- This target builds everything (refind_x64.efi, gptsync_x64.efi, and the filesystem drivers) with the GNU-EFI toolkit. -* tiano -- This deprecated target builds refind_x64.efi and gptsync_x64.efi - with the TianoCore toolkit by using custom Makefile rules, bypassing the - TianoCore toolkit's "build" command. The result is slightly faster - compilation in a more traditional Unix/Linux way; but the compilation - rules are fragile and work only with the UDK2014 toolkit. Also, the - gptsync_x64.efi program is not built on ARM64/AARCH64 because the build - process fails. (As hybrid MBRs are likely to be useless on this platform, - the inability to build gptsync_aa64.efi is no great loss.) +* tiano -- This deprecated, and increasingly unreliable, target builds + refind_x64.efi and gptsync_x64.efi with the TianoCore toolkit by using + custom Makefile rules, bypassing the TianoCore toolkit's "build" command. + The result is slightly faster compilation in a more traditional Unix/Linux + way; but the compilation rules are fragile and work only with the UDK2014 + toolkit. They worked with Ubuntu 18.04 and GCC 7.5, but now fail with + Ubuntu 20.04 and GCC 9.3. Also, the gptsync_x64.efi program is not built + on ARM64/AARCH64 because the build process fails. (As hybrid MBRs are + likely to be useless on this platform, the inability to build + gptsync_aa64.efi is no great loss.) * gptsync_tiano -- This target works like the preceding one, but builds only gptsync_x64.efi. * fs_tiano -- This target works like the preceding two, but builds the @@ -260,21 +303,23 @@ * edk2 -- Like the "tiano" target, this one builds with the TianoCore toolkit; but it employs a build method more like that favored by TianoCore. It relies on .dsc, .dec, and .inf files as well as the "build" - program that comes with TianoCore. This method works with UDK2014, - UDK2018, and the latest (as of July, 2017) EDK2 snapshot. It also works - under OS X, if TianoCore is properly prepared. One limitation is that - building with UDK2014 for ARM64/AARCH64 requires modifying the .inf files - to remove the reference to CompilerIntrinsicsLib. This build method is - also a little bit slower than the preceding ones, in part because - EVERYTHING is built; narrower targets simply copy fewer of the resulting - files from within the TianoCore directory tree. Note that this method, - unlike the preceding ones, requires WRITE access to the TianoCore build - tree, or at least to the Build and Conf subdirectories of that tree, as - well as to the root of the tree. (This method creates a symbolic link of - the main rEFInd directory into the root of the TianoCore tree, and the - build process creates a subdirectory called Build/Refind to hold temporary - files and the final .efi files. The "make" utility then copies these files - to the same locations used by the tiano and gnuefi targets.) + program that comes with TianoCore. This method works with UDK2014 and + UDK2018. It might work with more recent EDK2 snapshots, but as noted + earlier, my attempt to use a November 2020 snapshot failed because I + couldn't get it to compile. This method also works under macOS, if + TianoCore is properly prepared. One limitation is that building with + UDK2014 for ARM64/AARCH64 requires modifying the .inf files to remove the + reference to CompilerIntrinsicsLib. This build method is also a little bit + slower than the preceding ones, in part because EVERYTHING is built; + narrower targets simply copy fewer of the resulting files from within the + TianoCore directory tree. Note that this method, unlike the preceding + ones, requires WRITE access to the TianoCore build tree, or at least to + the Build and Conf subdirectories of that tree, as well as to the root of + the tree. (This method creates a symbolic link of the main rEFInd + directory into the root of the TianoCore tree, and the build process + creates a subdirectory called Build/Refind to hold temporary files and the + final .efi files. The "make" utility then copies these files to the same + locations used by the tiano and gnuefi targets.) * gptsync_edk2 -- This target copies just the gptsync_x64.efi binary to its final destination. * fs_edk2 -- This target works like the "edk2" target, but copies only @@ -350,9 +395,9 @@ "build" command. rEFInd can be compiled in this way. I am providing instructions on compiling in this way because it may work better than rEFInd's Makefiles in some cases, especially if you're using a non-Linux -platform for development. I've successfully compiled rEFInd 0.10.8 under OS -X in this way. (I present details and caveats shortly.) Note that the "edk2" -and related Makefile targets use this method behind the scenes. The +platform for development. I've successfully compiled rEFInd 0.10.8 under +macOS in this way. (I present details and caveats shortly.) Note that the +"edk2" and related Makefile targets use this method behind the scenes. The procedure is: 1. Download and prepare the TianoCore toolkit, as described earlier, @@ -401,18 +446,18 @@ Build/Refind/RELEASE_GCC49/X64/refind.efi Build/Refind/RELEASE_GCC49/X64/reiserfs.efi -I've tested this procedure under Ubuntu 16.04 with GCC 5.4.0 and under OS X +I've tested this procedure under Ubuntu 16.04 with GCC 5.4.0 and under macOS 10.11 with both Clang 7.0.0/XCode 7.1.1 and Clang 8.0.0/XCode 8.1.1. (See below for Mac caveats.) In theory, it might work under Windows, but if you use anything but a GCC- or Clang-derived compiler, there's a good chance you'll run into a compiler-specific code incompatibility. -Compiling rEFInd Under OS X -=========================== +Compiling rEFInd Under MacOS +============================ -Building under OS X is *NOT SUPPORTED.* I've tested this procedure and it -seems to work in minimal testing. My build under OS X required several +Building under macOS is *NOT SUPPORTED.* I've tested this procedure and it +seems to work in minimal testing. My build under macOS required several changes for compilation to succeed: * A relatively recent TianoCore EDK2 from the TianoCore git repository is @@ -436,7 +481,7 @@ prevented the rEFInd binary from compiling with "-Werror" intact related to ((sysv_abi)) declarations in mok/mok.h. This makes me think that the resulting binary may be incompatible with Shim, although I've not tested - this; my only testing of the binary built under OS X were on systems with + this; my only testing of the binary built under macOS were on systems with Secure Boot disabled or with the Secure Boot system under my complete control and without Shim installed. @@ -445,7 +490,8 @@ * I've been unable to get the Btrfs driver to build. To adjust the package to omit this driver, edit the RefindPkg.dsc file and remove the line near - the bottom that reads "RefindPkg/filesystems/btrfs.inf". + the bottom that reads "RefindPkg/filesystems/btrfs.inf". (This limitation + may be resolved with rEFInd 0.12.1, but I haven't tested it.) Note that if you want to use macOS or Windows to compile rEFInd, you might be able to create a project or Makefile for your non-GCC compiler or use a @@ -477,7 +523,7 @@ * Copy the icons subdirectory, including all its files, to EFI/refind. You'll then need to activate rEFInd in your EFI. This can be done with -tools such as "efibootmgr" under Linux or "bless" under OS X. See the +tools such as "efibootmgr" under Linux or "bless" under macOS. See the docs/refind/installing.html file for details. diff -Nru refind-0.12.0/debian/changelog refind-0.13.2/debian/changelog --- refind-0.12.0/debian/changelog 2020-07-22 21:00:56.000000000 +0000 +++ refind-0.13.2/debian/changelog 2021-10-25 22:44:40.000000000 +0000 @@ -1,3 +1,17 @@ +refind (0.13.2-1) unstable; urgency=medium + + [ Debian Janitor ] + * Remove constraints unnecessary since stretch: + + Build-Depends: Drop versioned constraint on debhelper. + + [ Tianon Gravi ] + * Update to 0.13.2 upstream release + - see http://www.rodsbooks.com/refind/revisions.html for changes + - "Updated CentOS Secure Boot (MOK) keys" (Closes: #958744) + * Backport "gnu-efi" fix (Closes: #995623) + + -- Tianon Gravi Mon, 25 Oct 2021 15:44:40 -0700 + refind (0.12.0-1) unstable; urgency=medium * Update to 0.12.0 upstream release diff -Nru refind-0.12.0/debian/control refind-0.13.2/debian/control --- refind-0.12.0/debian/control 2020-02-21 16:00:39.000000000 +0000 +++ refind-0.13.2/debian/control 2021-08-23 22:21:26.000000000 +0000 @@ -4,14 +4,14 @@ Section: admin Priority: optional Standards-Version: 4.3.0 -Build-Depends: debhelper (>= 9), gnu-efi +Build-Depends: debhelper, gnu-efi Homepage: https://www.rodsbooks.com/refind Vcs-Browser: https://salsa.debian.org/debian/refind Vcs-Git: https://salsa.debian.org/debian/refind.git Package: refind Architecture: amd64 arm64 i386 -Depends: debconf, efibootmgr, gdisk, openssl, ${misc:Depends} +Depends: debconf, efibootmgr, gdisk, mokutil, openssl, ${misc:Depends} Recommends: python3, sbsigntool Description: boot manager for EFI-based computers A graphical boot manager for EFI- and UEFI-based computers, such as all diff -Nru refind-0.12.0/debian/copyright refind-0.13.2/debian/copyright --- refind-0.12.0/debian/copyright 2020-03-17 21:27:08.000000000 +0000 +++ refind-0.13.2/debian/copyright 2021-08-23 22:21:26.000000000 +0000 @@ -325,6 +325,7 @@ icons/os_slackware.png icons/os_suse.png icons/os_ubuntu.png + icons/os_uefi.png icons/os_unknown.png icons/os_win8.png icons/os_xubuntu.png @@ -332,6 +333,7 @@ icons/tool_netboot.png icons/tool_shell.png icons/tool_windows_rescue.png + icons/vol_efi.png icons/vol_external.png icons/vol_internal.png icons/vol_net.png @@ -390,6 +392,11 @@ Copyright: 2008 Dan Rabbit License: GPL-2 +Files: icons/os_manjaro.png +License: public-domain + Claimed as public domain by source + (https://commons.wikimedia.org/wiki/File:Manjaro-logo.svg) + Files: icons/os_void.png License: public-domain Claimed as public domain by source diff -Nru refind-0.12.0/debian/patches/gcc-10.patch refind-0.13.2/debian/patches/gcc-10.patch --- refind-0.12.0/debian/patches/gcc-10.patch 2020-07-22 17:34:11.000000000 +0000 +++ refind-0.13.2/debian/patches/gcc-10.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From e34a16301f425f273a67ed3abbc45840bc82d892 Mon Sep 17 00:00:00 2001 -From: srs5694 -Date: Fri, 15 May 2020 12:34:14 -0400 -Subject: [PATCH] Fix GCC 10 compile problem - ---- - Make.common | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Make.common b/Make.common -index 3f0b919..95a3a97 100644 ---- a/Make.common -+++ b/Make.common -@@ -60,7 +60,7 @@ endif - # - - # ...for both GNU-EFI and TianoCore.... --OPTIMFLAGS = -Os -fno-strict-aliasing -+OPTIMFLAGS = -Os -fno-strict-aliasing -fno-tree-loop-distribute-patterns - CFLAGS = $(OPTIMFLAGS) -fno-stack-protector -fshort-wchar -Wall - - # ...for GNU-EFI.... --- -2.20.1 - diff -Nru refind-0.12.0/debian/patches/gnu-efi.patch refind-0.13.2/debian/patches/gnu-efi.patch --- refind-0.12.0/debian/patches/gnu-efi.patch 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/debian/patches/gnu-efi.patch 2021-10-25 22:23:58.000000000 +0000 @@ -0,0 +1,48 @@ +Origin: https://sourceforge.net/p/refind/code/ci/10f838a4cd132a5595acaf3c54c15782151a61df +Applied-Upstream: 0.13.3 +From 10f838a4cd132a5595acaf3c54c15782151a61df Mon Sep 17 00:00:00 2001 +From: Rod Smith +Date: Sat, 23 Oct 2021 10:17:23 -0400 +Subject: [PATCH] Fix compile problems with recent GNU-EFI versions + +--- + EfiLib/DevicePathUtilities.h | 13 ------------- + EfiLib/gnuefi-helper.c | 1 - + NEWS.txt | 3 +++ + include/version.h | 2 +- + 4 files changed, 4 insertions(+), 15 deletions(-) + +diff --git a/EfiLib/DevicePathUtilities.h b/EfiLib/DevicePathUtilities.h +index b559671..14ed797 100644 +--- a/EfiLib/DevicePathUtilities.h ++++ b/EfiLib/DevicePathUtilities.h +@@ -217,17 +217,4 @@ EFIAPI + --*/ + ; + +-typedef struct { +- EFI_DEVICE_PATH_UTILS_GET_DEVICE_PATH_SIZE GetDevicePathSize; +- EFI_DEVICE_PATH_UTILS_DUP_DEVICE_PATH DuplicateDevicePath; +- EFI_DEVICE_PATH_UTILS_APPEND_PATH AppendDevicePath; +- EFI_DEVICE_PATH_UTILS_APPEND_NODE AppendDeviceNode; +- EFI_DEVICE_PATH_UTILS_APPEND_INSTANCE AppendDevicePathInstance; +- EFI_DEVICE_PATH_UTILS_GET_NEXT_INSTANCE GetNextDevicePathInstance; +- EFI_DEVICE_PATH_UTILS_IS_MULTI_INSTANCE IsDevicePathMultiInstance; +- EFI_DEVICE_PATH_UTILS_CREATE_NODE CreateDeviceNode; +-} EFI_DEVICE_PATH_UTILITIES_PROTOCOL; +- +-extern EFI_GUID gEfiDevicePathUtilitiesProtocolGuid; +- + #endif +diff --git a/EfiLib/gnuefi-helper.c b/EfiLib/gnuefi-helper.c +index 246c261..8730403 100644 +--- a/EfiLib/gnuefi-helper.c ++++ b/EfiLib/gnuefi-helper.c +@@ -20,7 +20,6 @@ + #include "refit_call_wrapper.h" + #include "LegacyBios.h" + +-EFI_GUID gEfiDevicePathUtilitiesProtocolGuid = { 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; + EFI_GUID gEfiLegacyBiosProtocolGuid = { 0xdb9a1e3d, 0x45cb, 0x4abb, { 0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d }}; + + /** diff -Nru refind-0.12.0/debian/patches/series refind-0.13.2/debian/patches/series --- refind-0.12.0/debian/patches/series 2020-07-22 17:34:29.000000000 +0000 +++ refind-0.13.2/debian/patches/series 2021-10-25 22:24:02.000000000 +0000 @@ -1 +1 @@ -gcc-10.patch +gnu-efi.patch diff -Nru refind-0.12.0/docs/man/mkrlconf.8 refind-0.13.2/docs/man/mkrlconf.8 --- refind-0.12.0/docs/man/mkrlconf.8 2020-03-13 12:44:34.000000000 +0000 +++ refind-0.13.2/docs/man/mkrlconf.8 2021-03-14 00:00:22.000000000 +0000 @@ -1,7 +1,7 @@ -.\" Copyright 2015-2020 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" Copyright 2015-2021 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU Free Documentation License version 1.3 or any later version -.TH "MKRLCONF" "8" "0.12.0" "Roderick W. Smith" "rEFInd Manual" +.TH "MKRLCONF" "8" "0.13.2" "Roderick W. Smith" "rEFInd Manual" .SH "NAME" mkrlconf \- Create a Linux kernel configuration file for rEFInd .SH "SYNOPSIS" diff -Nru refind-0.12.0/docs/man/mvrefind.8 refind-0.13.2/docs/man/mvrefind.8 --- refind-0.12.0/docs/man/mvrefind.8 2020-03-13 12:44:25.000000000 +0000 +++ refind-0.13.2/docs/man/mvrefind.8 2021-03-14 00:00:15.000000000 +0000 @@ -1,7 +1,7 @@ -.\" Copyright 2015-2020 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" Copyright 2015-2021 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU Free Documentation License version 1.3 or any later version -.TH "MVREFIND" "8" "0.12.0" "Roderick W. Smith" "rEFInd Manual" +.TH "MVREFIND" "8" "0.13.2" "Roderick W. Smith" "rEFInd Manual" .SH "NAME" mvrefind \- Move a rEFInd installation from one location to another .SH "SYNOPSIS" diff -Nru refind-0.12.0/docs/man/refind-install.8 refind-0.13.2/docs/man/refind-install.8 --- refind-0.12.0/docs/man/refind-install.8 2020-03-13 12:44:15.000000000 +0000 +++ refind-0.13.2/docs/man/refind-install.8 2021-03-14 00:00:17.000000000 +0000 @@ -1,7 +1,7 @@ -.\" Copyright 2015-2020 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" Copyright 2015-2021 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU Free Documentation License version 1.3 or any later version -.TH "REFIND-INSTALL" "8" "0.12.0" "Roderick W. Smith" "rEFInd Manual" +.TH "REFIND-INSTALL" "8" "0.13.2" "Roderick W. Smith" "rEFInd Manual" .SH "NAME" refind-install \- Install rEFInd to the ESP and create an NVRAM entry .SH "SYNOPSIS" diff -Nru refind-0.12.0/docs/man/refind-mkdefault.8 refind-0.13.2/docs/man/refind-mkdefault.8 --- refind-0.12.0/docs/man/refind-mkdefault.8 2020-03-13 12:44:04.000000000 +0000 +++ refind-0.13.2/docs/man/refind-mkdefault.8 2021-03-14 00:00:06.000000000 +0000 @@ -1,7 +1,7 @@ -.\" Copyright 2016-2020 Roderick W. Smith (rodsmith@rodsbooks.com) +.\" Copyright 2016-2021 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU Free Documentation License version 1.3 or any later version -.TH "REFIND-MKDEFAULT" "8" "0.12.0" "Roderick W. Smith" "rEFInd Manual" +.TH "REFIND-MKDEFAULT" "8" "0.13.2" "Roderick W. Smith" "rEFInd Manual" .SH "NAME" refind-mkdefault \- Set rEFInd as the default EFI boot option .SH "SYNOPSIS" diff -Nru refind-0.12.0/docs/refind/bootcoup.html refind-0.13.2/docs/refind/bootcoup.html --- refind-0.12.0/docs/refind/bootcoup.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/bootcoup.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

Originally written: 4/24/2016; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

+3/13/2021, referencing rEFInd 0.13.2

This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

@@ -180,7 +180,7 @@
  • The Unstable State: Dealing With Persistent Boot Coups
  • -
  • Managing Boot Coups with fallback.efi/fbx86.efi
  • +
  • Managing Boot Coups with fallback.efi/fbx64.efi
  • @@ -191,7 +191,7 @@

    This page describes tools and techniques you can use to keep rEFInd set as your default boot manager, or at least to recover it as the default boot option if something else takes over. This page is organized by OS, describing the tools and techniques you can use in each OS to recover from a boot coup—or in some cases, to prevent one from occurring. I begin and end with information on firmware-based tools, though. Chances are you should not read this page straight through; instead, peruse the Contents to the left and pick an OS and, perhaps, a recovery tool or technique you wish to pursue and read the relevant section. In most cases, the recovery technique is fairly quick and painless, once you understand how to do it. Note also that, in extreme cases, a full rEFInd re-installation may be required. It may also be easier to re-run refind-install than to learn about esoteric commands such as efibootmgr, bless, or bcdedit.

    -

    Evading the Guards: Performing a One-Time Boot to Your Desired OS

    +

    Evading the Guards: Performing a One-Time Boot to Your Desired OS

    Most EFIs provide their own built-in boot managers. These tools are primitive, and in some cases they can be difficult to reach, but they can be useful if you need to bypass a new system default in order to boot an OS that has the tools you need to control the boot process.

    @@ -332,7 +332,9 @@

    The Startup Disk utility appears in the System Preferences tool. Unfortunately, it will likely be useless if you installed rEFInd using refind-install and its default options, since Startup Disk is designed to switch between macOS installations; it's not smart enough to detect a rEFInd installation and re-activate it.

    -

    If, however, you installed rEFInd by using the --ownhfs option to refind-install, your rEFInd installation volume should show up as an option in the Startup Disk utility. You should be able to click on it and then click Restart. Note that the name of the rEFInd volume may not be rEFInd, as it is in this screen shot; the name will match whatever volume holds rEFInd on your computer.

    + + +

    If, however, you installed rEFInd by using the --ownhfs option to refind-install, your rEFInd installation volume may show up as an option in the Startup Disk utility. You should be able to click on it and then click Restart. Note that the name of the rEFInd volume may not be rEFInd, as it is in this screen shot; the name will match whatever volume holds rEFInd on your computer.


    Startup Disk may enable you to reset rEFInd to being
@@ -344,7 +346,7 @@
 <h3>Using <tt>bless</tt> to Adjust Your Boot Priority</h3>
 </a>
 
-<p>The more general solution to resetting rEFInd as the default boot manager from macOS is to follow a subset of the <a href=manual macOS installation instructions. Unfortunately, some details depend on where rEFInd is installed—on the ESP, on the main macOS root (/) partition, or on a separate HFS+ volume. If rEFInd is installed on its own HFS+ partition, using Startup Disk, as described in the previous section, is likely to be the easier solution. For the other two options, you should first figure out where rEFInd is installed and then follow this procedure:

    +

    The more general solution to resetting rEFInd as the default boot manager from macOS is to follow a subset of the manual macOS installation instructions. Unfortunately, some details depend on where rEFInd is installed—on the ESP, on the main macOS root (/) partition, or on a separate HFS+ volume. If rEFInd is installed on its own HFS+ partition, using Startup Disk, as described in the previous section, is likely to be the easier solution—if that approach works for you. For the other two options, or for a dedicated HFS+ installation if Startup Disk balks at making it bootable, you should first figure out where rEFInd is installed and then follow this procedure:

      @@ -448,9 +450,9 @@
      rEFInd's EFI boot order maintenance tool enables you to set a default boot option or delete unwanted options.

      -

      The list provides up to four pices of information about each boot entry: its boot number (such as Boot0004; its description (such as rEFInd Boot Manager, its filename (such as EFI\refind\refind_x64.efi, and its volume name (such as ESP). Sometimes one or more of these pieces of information can be missing. In the preceding example, for instance, the final four items on the list lack filenames and volume names. (This is common for entries created by the firmware for its own hardware devices.) If you can't figure out which entry you want to delete or elevate to the first position, I recommend booting into Linux or Windows and using efibootmgr or the commercial EasyUEFI, respectively.

      +

      The list provides up to four pices of information about each boot entry: its boot number (such as Boot0004); its description (such as rEFInd Boot Manager); its filename (such as EFI\refind\refind_x64.efi); and its volume name (such as ESP). Sometimes one or more of these pieces of information can be missing. In the preceding example, for instance, the final four items on the list lack filenames and volume names. (This is common for entries created by the firmware for its own hardware devices.) If you can't figure out which entry you want to delete or elevate to the first position, I recommend booting into Linux or Windows and using efibootmgr or the commercial EasyUEFI, respectively.

      -

      You can use the arrow keys to move up and down in the list to select a boot entry. Once you've picked an option, you can perform either of three actions from this menu:

      +

      You can use the arrow keys to move up and down in the list to select a boot entry. Once you've picked an option, you can perform any of three actions from this menu:

        @@ -462,7 +464,7 @@
      -

      rEFInd's EFI boot maintenance tool does not enable you to fine-tune the order of multiple items all at once. You can do so by making repeated calls to the program, however. For instance, to convert the entries shown earlier to a boot order of 0006, 0005, 0004, you would call the function twice — once to move 0005 to the top of the list and then to move 0006 there.

      +

      rEFInd's EFI boot maintenance tool does not enable you to fine-tune the order of multiple items all at once. You can do so by making repeated calls to the feature, however. For instance, to convert the entries shown earlier to a boot order of 0006, 0005, 0004, you would call the function twice — once to move 0005 to the top of the list and then to move 0006 there.

      As with most of the fixes described on this page, this method of recovering from a boot coup will not protect you from future boot coups. Preparing a USB flash drive or CD-R with rEFInd on it can help you when boot coups occur, though, especially if the boot coup corrupts your boot list so that you can't start rEFInd. If you can boot from the external medium, you can use it to launch rEFInd and adjust the boot order, or even completely re-install rEFInd from the external disk.

      @@ -500,13 +502,29 @@

      This procedure is only an example for one EFI. In fact, some EFIs, including the one in the ASUS P8 H77-I, feature multiple user interface modes. The ASUS has an "Advanced" mode, for instance, in which the procedure would be slightly different. They key point, though, is to locate whatever menu displays the boot order and use that menu to adjust it. Such a menu may be shown on the main screen, as in the case of the ASUS' "EZ-Mode," or on a menu you must select—often called "Boot" or something similar. Some EFIs, particularly for low-end fully-assembled desktop and laptop computers, lack this functionality altogether.

      -

      As with most other fixes described on this page, this one won't protect you from future boot coups. Most boot coups are caused by actions of an OS, so prevention must be handled on an OS-by-OS basis.

      +

      One other example (documented in more detail by Macworld) deserves mention. On many (maybe all) Intel-based Macs, you can adjust the boot order as follows:

      + +
        + +
      1. Start with the computer turned off, then power it on; or restart it.
      2. + +
      3. Immediately press and hold the Option (or Alt) key to activate the Mac's built-in boot manager.
      4. + +
      5. Hold the Control key as you use the arrow keys to move the cursor over the item you want to make the default.
      6. + +
      7. Press the Enter key to boot the selected item.
      8. + +
      + +

      This Mac-specific procedure presents only a limited selection of options. In particular, it will show macOS, bootable removable disks, and rEFInd installed to a dedicated HFS+ partition via the refind-install --ownhfs procedure; but it will not show rEFInd (or other boot programs) installed to the ESP, even if those options have EFI boot entries. Thus, it may not do what you need; but if you install rEFInd to an HFS+ volume, this procedure could be a useful tool for recovering from a boot coup.

      + +

      As with most other fixes described on this page, recovering from a boot coup using your firmware's setup tools won't protect you from future boot coups. Most boot coups are caused by actions of an OS, so prevention must be handled on an OS-by-OS basis.

      Using an EFI Shell to Adjust Your Boot Priority

      -

      Version 2 of the EFI shell provides a command, bcfg, which can adjust the EFI boot order. Unfortunately, this tool is not present in version 1 of the EFI shell, and version 2 is reliable only with EFI version 2.3 and later, although at least one binary claims to work around that problem. To date (early 2020), all Intel-based Macs use EFI 1.1, and many PCs sold prior to Windows 8's release use UEFI (EFI 2.x) versions prior to 2.3. Thus, this approach may not work for you.

      +

      Version 2 of the EFI shell provides a command, bcfg, which can adjust the EFI boot order. Unfortunately, this tool is not present in version 1 of the EFI shell, and version 2 is reliable only with EFI version 2.3 and later, although at least one binary claims to work around that problem. To date (early 2021), most Intel-based Macs use EFI 1.1 (although some newer Intel-based Macs now have UEFI [EFI 2.x] firmware), and many PCs sold prior to Windows 8's release use UEFI versions prior to 2.3. Thus, this approach may not work for you.

      Even if your computer works with a version 2 shell, it may not have one built in. In fact, most EFIs I've seen lack a built-in shell. If a shell is available, it should appear on the EFI's built-in boot manager, as described earlier, in Evading the Guards: Performing a One-Time Boot to Your Desired OS. If a shell is not built into your firmware, you can add one; here are a couple of links that may be helpful:

      @@ -564,10 +582,10 @@

      Because of the complexity of the procedure for starting an EFI shell if one is not already prepared, this procedure works best if one is built into your EFI or if you already have one ready.

      -

      The Unstable State: Dealing With Persistent Boot Coups

      +

      The Unstable State: Dealing With Persistent Boot Coups

      -

      If your computer simply refuses to boot into rEFInd, chances are your firmware is either ignoring its boot entries or forgetting them. For the most part, which is the case doesn't really matter, since the solutions are similar for both cases. There are a few obscure exceptions, though; for instance, an entry will be ignored if it's malformed—such as if the filename specification includes a typo. Also, there is at least one known bug that causes the computer to ignore boot loader entries except for those named "Windows Boot Manager" or "Red Hat Enterprise Linux." Such problems can be fixed by creating a fresh NVRAM entry for rEFInd that fix the typo or give the entry the name that the EFI expects (even if it's a misleading name).

      +

      If your computer simply refuses to boot into rEFInd, chances are your firmware is either ignoring its boot entries or forgetting them. For the most part, which is the case doesn't really matter, since the solutions are similar for both cases. There are a few exceptions, though; for instance, an entry will be ignored if it's malformed—such as if the filename specification includes a typo. Also, there is at least one known bug that causes the computer to ignore boot loader entries except for those named "Windows Boot Manager" or "Red Hat Enterprise Linux." Such problems can be fixed by creating a fresh NVRAM entry for rEFInd that fix the typo or give the entry the name that the EFI expects (even if it's a misleading name).

      More common are problems in which the firmware ignores or forgets its boot entries. Such problems used to be quite common, but are becoming rarer as manufacturers (slowly) improve their products. My general advice for fixing such problems is to attempt each of the following, in more-or-less the stated order:

      @@ -581,7 +599,7 @@
    1. Return the computer for a refund. If none of the preceding steps works, chances are your firmware is just plain defective. Note that by "defective" I mean "defective by design," not a sample defect, so you should not exchange the computer for another of the same model. (Indeed, even another model of the same brand may suffer from the same problem.) Your best bet in this case is to return the product to the store for a refund and write to the manufacturer about the problem. Manufacturers will not fix problems that they don't know exist, so informing them of the problem is important. Unfortunately, many people learn of such problems only after having owned a computer for months, so a return is not always practical....
    2. -
    3. Use the fallback.efi program. This approach is described shortly, in Managing Boot Coups with fallback.efi/fbx86.efi.
    4. +
    5. Use the fallback.efi program. This approach is described shortly, in Managing Boot Coups with fallback.efi/fbx64.efi.
    6. Use a fallback filename. You can use mvrefind in Linux to rename rEFInd to use either of two fallback filenames: @@ -602,14 +620,14 @@

      Another thing that can produce symptoms similar to a persistent boot coup is Secure Boot. If Secure Boot is enabled on your computer and you install rEFInd without a Shim or PreLoader program, your computer will probably refuse to launch rEFInd. In this case, inserting Shim or PreLoader into the boot process, as described on the rEFInd Secure Boot page, normally overcomes this problem. On rare occasions, though, Shim or PreLoader won't work with a particular computer. In such a case, you may need to disable Secure Boot. Note that this level of Secure Boot malfunction is quite rare. I see many posts in online forums that jump to the conclusion that Secure Boot is causing a problem, when in fact there's another more likely cause. Thus, I urge you to investigate other possibilities before concluding that Secure Boot is causing an inability to boot rEFInd.

      -

      Managing Boot Coups with fallback.efi/fbx86.efi

      +

      Managing Boot Coups with fallback.efi/fbx64.efi

      One type of boot problem is similar to a boot coup, but has a unique cause: Some EFIs, especially older ones (mostly from 2012 or earlier) have a tendency to forget their NVRAM entries. Such computers boot from the fallback boot loader (EFI/BOOT/bootx64.efi) or from the Microsoft boot loader (EFI/Microsoft/Boot/bootmgfw.efi), but that's about it. A similar problem is that some computers remove invalid boot entries from their boot lists. This is helpful if you delete a boot loader, but it's less than helpful if you temporarily unplug your boot disk and then plug it back in.

      -

      In either of these cases, your computer may boot to an unwanted OS or completely fail to boot. One solution to this problem is to install rEFInd to the fallback filename, as described earlier, in The Unstable State: Dealing With Persistent Boot Coups. Another is to use an EFI program called fallback.efi, fbx64.efi, or an equivalent filename on other platforms. To use this program, you would install it to EFI/BOOT on the ESP, either renaming it to bootx64.efi (that is, fallback.efi uses the fallback filename) or installing Shim as bootx64.efi. Shim will try to launch fallback.efi or fbx64.efi when it boots, so either way, this program will launch. (Be sure to match your Shim and fallback.efi/fbx864.efi binaries, so that Shim launches the correct program!) For simplicity, I call this program fallback.efi hereafter.

      +

      In either of these cases, your computer may boot to an unwanted OS or completely fail to boot. One solution to this problem is to install rEFInd to the fallback filename, as described earlier, in The Unstable State: Dealing With Persistent Boot Coups. Another is to use an EFI program called fallback.efi, fbx64.efi, or an equivalent filename on other platforms. To use this program, you would install it to EFI/BOOT on the ESP, either renaming it to bootx64.efi (that is, fallback.efi uses the fallback filename) or installing Shim as bootx64.efi. Shim will try to launch fallback.efi or fbx64.efi when it boots, so either way, this program will launch. (Be sure to match your Shim and fallback.efi/fbx64.efi binaries, so that Shim launches the correct program!) For simplicity, I call this program fallback.efi hereafter.

      -

      When fallback.efi launches, it reads every subdirectory of EFI on the ESP except for EFI/BOOT and looks for a file called BOOT.CSV. If this file exists and contains a UCS-2 (UTF-16 also seems to work) text file, that file is read and used to create a new NVRAM boot variable. The format of BOOT.CSV is simple; it consists of one or more lines, each of which consists of four comma-separated fields:

      +

      When fallback.efi launches, it reads every subdirectory of EFI on the ESP except for EFI/BOOT and looks for a file called BOOT.CSV, BOOTX64.CSV, or file with a similar but architecture-appropriate name. If this file exists and contains a UCS-2 (UTF-16 also seems to work) text file, that file is read and used to create a new NVRAM boot variable. The format of BOOT.CSV is simple; it consists of one or more lines, each of which consists of four comma-separated fields:

        @@ -636,9 +654,9 @@

        Depending on permissions on your ESP and the account you use, you could do the same thing in a single step by writing directly to the ESP.

        -

        Note that fallback.efi can create boot coups, in addition to fixing them. If it's run inappropriately, this program can modify your NVRAM-based boot list, causing something you don't want to run to become the default boot loader. Thus, if you're experiencing boot coups, you may want to check for the presence of this program and either delete it or adjust the BOOT.CSV files on your ESP. You can find all the BOOT.CSV files as follows, assuming the ESP is mounted at /boot/efi:

        +

        Note that fallback.efi can create boot coups, in addition to fixing them. If it's run inappropriately, this program can modify your NVRAM-based boot list, causing something you don't want to run to become the default boot loader. Thus, if you're experiencing boot coups, you may want to check for the presence of this program and either delete it or adjust the BOOT.CSV files on your ESP. You can find all the BOOT.CSV (and similarly-named) files as follows, assuming the ESP is mounted at /boot/efi:

        -
        $ find /boot/efi -iname BOOT.CSV
        +
        $ find /boot/efi -iname "BOOT*.CSV"

        By default, Fedora and its relatives install fallback.efi in the fallback position (typically launched by Shim, actually) and set up a @@ -654,7 +672,7 @@


        -

        copyright © 2016–2020 by Roderick W. Smith

        +

        copyright © 2016–2021 by Roderick W. Smith

        This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

        diff -Nru refind-0.12.0/docs/refind/bootmode.html refind-0.13.2/docs/refind/bootmode.html --- refind-0.12.0/docs/refind/bootmode.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/bootmode.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

        Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

        +3/13/2021, referencing rEFInd 0.13.2

        This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

        @@ -153,12 +153,14 @@

        Unfortunately, determining which mode you're using can be tricky; the clues are subtle or hidden in ways that require specialized knowledge to extract. This page will help you figure it out. I first present general information on identifying your hardware's capabilities. I then describe ways to identify your current boot mode in both Linux and Windows.

        -

        Identifying Your Hardware's Capabilities

        +

        Identifying Your Hardware's Capabilities

        -

        Let's get the easy case out of the way: If you have a Macintosh with an Intel CPU, it's got EFI capabilities, and you'll be able to use rEFInd. Earlier Macs with PowerPC CPUs use OpenFirmware, and rEFInd can't be used with them. If your computer shipped new with Windows 8 or later, it almost certainly supports EFI; Microsoft requires that computers that bear a Windows 8 (or later) logo support EFI, and boot in EFI mode. Most x86 and x86-64 computers released after late 2011 support EFI, although there are some laggards, particularly among server-class machines. Almost everything sold new since about 2014 is based on a decent EFI implementation.

        +

        Let's get the easy case out of the way: If you have a Macintosh with an Intel CPU, it's got EFI capabilities, and you'll be able to use rEFInd. Earlier Macs with PowerPC CPUs use OpenFirmware, not EFI, and rEFInd can't be used with them. The latest Macs (introduced late in 2020) use ARM CPUs — but as of early 2021, many Macs are still based on Intel CPUs. I don't yet own one of the new ARM-based Macs, but my understanding is that they are also EFI-based; however, I don't know if the current ARM build of rEFInd will run on them, and it's still early days for multi-booting such computers. See this blog post for information on an early attempt (which does not seem to use rEFInd). If you want to use rEFInd on one of these new ARM-based Macs, you'll definitely be playing on the "bleeding edge."

        -

        For everything else, it can be harder to tell. Your best bet is to locate a PDF version of your computer's or motherboard's manual and search it for the string EFI. Checking your firmware's options via the firmware setup utility (typically access by pressing Del, F2, F10, or F12 at boot time) is also worth doing, but you'll need to check every option yourself. Most EFI-enabled PCs include at least one reference to an option you can set; however, manuals and firmware setup tools often don't make a big deal of this feature, particularly on boards with relatively primitive EFI support. For instance, the manual for a Gigabyte GA-78LMT-S2P motherboard includes the following paragraph, on p. 28:

        +

        If your computer shipped new with Windows 8 or later, it almost certainly supports EFI; Microsoft requires that desktop and laptop computers that bear a Windows 8 (or later) logo support EFI, and boot in EFI mode. Most x86 and x86-64 computers released after late 2011 support EFI, although there are some laggards, particularly among server-class machines. Almost everything sold new since about 2014 is based on a decent EFI implementation.

        + +

        For everything else, it can be harder to tell. Your best bet is to locate a PDF version of your computer's or motherboard's manual and search it for the string EFI. Checking your firmware's options via the firmware setup utility (typically access by pressing Del, Esc, F2, F10, or F12 at boot time) is also worth doing, but you'll need to check every option yourself. Most EFI-enabled PCs include at least one reference to an option you can set; however, manuals and firmware setup tools often don't make a big deal of this feature, particularly on boards with relatively primitive EFI support. For instance, the manual for a Gigabyte GA-78LMT-S2P motherboard includes the following paragraph, on p. 28:

          @@ -172,7 +174,7 @@

          Understated EFI features often indicate a slapdash approach to EFI. Such systems sometimes implement EFI as a layer atop a conventional BIOS. More modern EFIs, though, completely replace the BIOS. Some manufacturers, such as ASUS and its sibling ASRock, are now actively promoting their more advanced EFI implementations. Such products often come with flashy new GUIs in their firmware.

          -

          Positive identification of EFI support in your firmware does not guarantee that your current OSes are booting in EFI mode. (MacOS booting on a Mac is an exception to this rule, though.) For that, you'll need to run some tests in your running OSes.

          +

          Positive identification of EFI support in your firmware does not guarantee that your current OSes are booting in EFI mode. (MacOS booting on an Intel-based Mac is an exception to this rule, though.) For that, you'll need to run some tests in your running OSes.

          Identifying Your Linux Boot Mode

          @@ -274,7 +276,7 @@
          -

          copyright © 2012–2020 by Roderick W. Smith

          +

          copyright © 2012–2021 by Roderick W. Smith

          This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

          @@ -282,7 +284,7 @@

          Go to the main rEFInd page

          -

          Learn how to use rEFInd

          +

          rEFInd Features

          Return to my main Web page.

          diff -Nru refind-0.12.0/docs/refind/configfile.html refind-0.13.2/docs/refind/configfile.html --- refind-0.12.0/docs/refind/configfile.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/configfile.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

          Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

          +3/13/2021, referencing rEFInd 0.13.2

          This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

          @@ -140,6 +140,8 @@
        • Creating Submenu Entries
        • +
        • Using Firmware Boot Options
        • +
        • Adjusting the Default Boot Option
        @@ -151,33 +153,42 @@

        Broadly speaking, rEFInd's configuration file is broken down into two sections: global options and OS stanzas. The global options section sets options that apply globally—to set the timeout period, enable graphics or text mode, and so on. OS stanzas are optional, but if present, they enable you to add new boot options or replace the auto-detected options with customized ones. Both sections include configuration lines and comment lines, the latter being denoted by a leading hash mark (#). rEFInd ignores comment lines, so you can add explanatory text. The default configuration file includes numerous comments explaining each of the options.

        -

        Hiding and Displaying EFI Boot Loaders

        +

        Hiding and Displaying EFI Boot Loaders

        A common complaint among rEFInd users is that rEFInd displays too many boot options. This problem is getting worse as OS vendors deliver more and more tools in the form of EFI programs installed on the ESP. It's difficult for me to keep up with this flood, and what one person considers a necessary program another may consider pointless clutter, making it hard to set useful defaults. Fortunately, rEFInd provides several ways to hide OSes—or to make them appear, if they don't by default. Methods to do this include:

          - + -
        • Hiding entries dynamically—rEFInd 0.11.0 introduced a dynamic tag hiding feature. To use it, highlight a tag and hit the Delete (on PCs) or minus (-) key. (The Delete key on Macs is the Backspace key on PCs, and will not work for this; however, some Mac keyboards have a key marked Del that will do the job.) rEFInd will ask for confirmation. If you give it, the OS or external tool tag will disappear, and should remain hidden indefinitely. (Note that you cannot hide built-in tools, such as the About/Info display and the reboot-to-firmware option, in this way.) rEFInd stores the list of EFI OS tags so hidden in NVRAM or on disk (as determined by the use_nvram token in refind.conf) using the HiddenTags variable, BIOS/CSM/legacy OS tags as HiddenLegacy, and tool tags as HiddenTools. If you want to recover a tag you've hidden, you can do so by using the hidden tag maintenance function, which appears on the second row of the rEFInd menu as a recycling symbol. You can disable both the ability to hide tags and to recover them by uncommenting the scanfor item in refind.conf and ensuring that hidden_tags is not among the options. (Even if you disable this feature, rEFInd continues to honor tags it finds in NVRAM or on disk.)
        • +
        • Hiding entries dynamically—rEFInd 0.11.0 introduced a dynamic tag hiding feature. To use it, highlight a tag and hit the Delete (on PCs) or minus (-) key. (The Delete key on Macs is the Backspace key on PCs, and will not work for this; however, some Mac keyboards have a key marked Del that will do the job.) rEFInd will ask for confirmation. If you give it, the OS or external tool tag will disappear, and should remain hidden indefinitely. (Note that you cannot hide built-in tools, such as the About/Info display and the reboot-to-firmware option, in this way.) rEFInd stores the list of EFI OS tags so hidden in NVRAM or on disk (as determined by the use_nvram token in refind.conf) using the HiddenTags variable, BIOS/CSM/legacy OS tags as HiddenLegacy, hidden firmware reboot tags using the HiddenFirmware variable, and tool tags as HiddenTools. If you want to recover a tag you've hidden, you can do so by using the hidden tag maintenance function, which appears on the second row of the rEFInd menu as a recycling symbol. You can disable both the ability to hide tags and to recover them by uncommenting the showtools item in refind.conf and ensuring that hidden_tags is not among the options. (Even if you disable this feature, rEFInd continues to honor tags it finds in NVRAM or on disk.)
        • - +
        • Moving, deleting, or renaming files—By default, rEFInd scans all the filesystems it can read for boot loaders. It scans most of the subdirectories of the EFI directory, as well as the root (/) and boot directories, on every filesystem it can access for files with names that end in .efi or that begin with vmlinuz, bzImage, or kernel. (rEFInd gives special treatment to the EFI/tools subdirectory, where it looks for system tools rather than boot loaders.) Thus, you can delete EFI program files, move them out of the directory tree that rEFInd scans, or rename them so that they don't have .efi extensions. Note that rEFInd does not scan its own directory or the EFI/tools directory, so those can be good places to stash seldom-used EFI binaries.
        • -
        • dont_scan_volumes—This token in refind.conf specifies volumes that rEFInd will not scan. For EFI boot loaders, you can identify a volume by its filesystem label, partition name, or partition unique GUID value. On Macs, you can identify BIOS/CSM/legacy-mode OSes to hide by specifying any unique subset of the OS tag description shown in the rEFInd main menu. In either case, this token takes a comma-delimited list, as in dont_scan_volumes ESP7,BadVolume to blacklist the ESP7 and BadVolume partitions. This token cannot be used to hide BIOS/CSM/legacy-mode loaders on UEFI-based PCs.
        • +
        • dont_scan_volumes—This token in refind.conf specifies volumes that rEFInd will not scan. For EFI boot loaders, you can identify a volume by its filesystem label, partition name, or partition unique GUID value. On Macs, you can identify BIOS/CSM/legacy-mode OSes to hide by specifying any unique subset of the OS tag description shown in the rEFInd main menu. In either case, this token takes a comma-delimited list, as in dont_scan_volumes ESP7,BadVolume to exclude the ESP7 and BadVolume partitions. This token cannot be used to hide BIOS/CSM/legacy-mode loaders on UEFI-based PCs.
        • dont_scan_dirs—This token provides finer-grained control than the preceding one; you identify directories with this option. For instance, you might specify dont_scan_dirs EFI/ignore,BigDisk:/EFI/OldOS to ignore the EFI/ignore directory on all volumes and the EFI/OldOS directory on the BigDisk volume. This token cannot be used to hide BIOS/CSM/legacy-mode loaders.
        • dont_scan_files—You can hide individual files with this token. Files can be specified alone, with a leading directory path, or with a leading directory path and volume name or GUID. For instance, dont_scan_files badloader.efi,EFI/ignoreme/boring.efi,MyDisk:/EFI/someos/grubx64.efi causes badloader.efi to be ignored in any location, EFI/ignoreme/boring.efi to be ignored on any disk, and EFI/someos/grubx64.efi to be ignored only on MyDisk. As with the previous items, you can identify a disk by filesystem label, partition name, or partition unique GUID.
        • +
        • dont_scan_firmware—This token specifies strings that match (case-insensitively) as substrings of EFI firmware boot options that are to be excluded from automatic scanning when scanfor specifies firmware as an option.
        • +
        • also_scan_dirs—This token does the opposite of the preceding ones: It adds a directory to the scan list, so that rEFInd can locate boot loaders stored in unusual locations. This token takes a directory path, and optionally a name (filesystem or partition), but cannot currently take a GUID value as a volume identifier.
        • -
        • scanfor—This token identifies the types of OSes for which rEFInd scans, and the types of devices on which it scans. On Macs, BIOS/CSM/legacy-mode scans are currently enabled by default; but you may want to disable this support by uncommenting the scanfor line and ensuring the hdbios, biosexternal, and cd are not present. BIOS-mode boot scans are disabled by default on UEFI-based PCs, so if you want to boot BIOS-mode OSes, you must uncomment scanfor and add an appropriate option.
        • +
        • scanfor—This token identifies the types of OSes for which rEFInd scans, and the types of devices on which it scans. On Macs, BIOS/CSM/legacy-mode scans are currently enabled by default; but you may want to disable this support by uncommenting the scanfor line and ensuring the hdbios, biosexternal, and cd are not present. BIOS-mode boot scans are disabled by default on UEFI-based PCs, so if you want to boot BIOS-mode OSes, you must uncomment scanfor and add an appropriate option. The firmware option is disabled by default on all computers; but if you add this option, rEFInd will present boot options corresponding to those defined by the firmware's built-in boot manager. (This topic is covered in more detail later, in Using Firmware Boot Options.)
        +

        Overall, dynamic hiding is the quickest and easiest way to hide one or two unwanted entries — you can do it from within rEFInd, with just a few keystrokes. Other methods may be preferable if you want to hide multiple tags or if a tag name changes unpredictably (like a Linux kernel with a version number in its filename, which changes whenever the kernel is updated). Changing scanfor is also appropriate if you want to hide all boot tags related to certain boot methods, such as all external devices or all BIOS/CSM/legacy boot options.

        +

        Setting OS Icons

        @@ -192,17 +203,17 @@
      • You can place a boot loader in a directory with a name that matches one of rEFInd's standard icons, which take names of the form os_name.icns or os_name.png. To use such an icon, you would place the boot loader in the directory called name. For instance, the boot loader in the EFI/freebsd directory will use rEFInd's os_freebsd.png icon.
      • -
      • You can give the filesystem from which the boot loader is loaded a name that matches the OS name component of the icon filename. For instance, if you call your boot filesystem CentOS, it matches the os_centos.png icon. This match is performed on a word-by-word basis within the name, with "words" being delimited by spaces, dashes (-), and underscores (_). Thus, a volume called Debian-boot will match os_debian.png or os_boot.png.
      • +
      • You can give the filesystem from which the boot loader is loaded a name that matches the OS name component of the icon filename. For instance, if you call your boot filesystem CentOS, it matches the os_centos.png icon. This match is performed on a word-by-word basis within the name, with "words" being delimited by spaces, dashes (-), colons (:), and underscores (_). Thus, a volume called Debian-boot will match os_debian.png or os_boot.png.
      • You can give the GPT partition from which the boot loader is loaded a name that matches the OS name component of the icon filename. This works much like the previous method, except that you'd use a tool like gdisk or parted to set the partition's name, rather than tune2fs or GParted to set the filesystem's name. Note that rEFInd ignores some common default partition names (namely Microsoft basic data, Linux filesystem, and Apple HFS/HFS+) when implementing this rule, since other rules are likely to produce more accurate results.
      • rEFInd attempts to guess the Linux distribution based on data in the /etc/os-release file. This file will only be accessible if a separate /boot partition is not used, though. Manually adjusting the os-release file to change an OS icon in rEFInd is not recommended.
      • -
      • Certain boot loaders have hard-coded icons associated with them. For instance, filenames beginning with vmlinuz or bzImage acquire Linux "Tux" icon and the bootmgfw.efi loader acquires a Windows icon. Fedora and Red Hat kernels can be identified by the presence of .fc or .el strings in their filenames, and so acquire suitable icons automatically. For the most part, these are the associations you want to overcome with the preceding rules, but sometimes renaming a boot loader to a more conventional name is the better approach. Renaming a locally-compiled kernel so that it acquires a Fedora or Red Hat icon is reasonable, but I don't recommend renaming precompiled kernels unless you also manually copy them to the ESP.
      • +
      • Certain boot loaders have hard-coded icons associated with them. For instance, filenames beginning with vmlinuz or bzImage acquire the Linux "Tux" icon and the bootmgfw.efi loader acquires a Windows icon. Fedora and Red Hat kernels can be identified by the presence of .fc or .el strings in their filenames, and so acquire suitable icons automatically. For the most part, these are the associations you want to overcome with the preceding rules, but sometimes renaming a boot loader to a more conventional name is the better approach. Renaming a locally-compiled kernel so that it acquires a Fedora or Red Hat icon is reasonable, but I don't recommend renaming precompiled kernels unless you also manually copy them to the ESP.
      -

      As a special case, rEFInd assigns icons to the Windows and macOS boot loaders based on their conventional locations, so they get suitable icons even if they don't follow these rules.

      +

      As a special case, rEFInd assigns icons to the Windows and macOS boot loaders based on their conventional locations, so they get suitable icons even if they don't follow these rules. Many of these methods don't apply to the icons assigned to boot tags based on the firmware's built-in boot manager, since there is no disk scanning associated with this detection method. For such tags, your only chance to change their icons is to create an icon file that matches a word in the description, such as os_lenovo.png to match an item called Lenovo Diagnostics. The icon for BIOS/CSM/legacy-mode boot options on UEFI-based PCs cannot be changed.

      These icon files should be in Apple's ICNS format, Portable Network Graphics (PNG) format, bitmap image file (BMP) format, or Joint Photographic Experts Group (JPEG) format, depending on the filename extension. As a general rule, PNG and ICNS files work best for icons, because they both support transparency, which is highly desirable in rEFInd icons, especially when the icon is shown against a full-screen banner. ICNS is an Apple-specific format, whereas PNG is cross-platform. BMP and JPEG files may be used as icons, but rEFInd does not support transparency with these formats. rEFInd relies on the LodePNG and NanoJPEG libraries for PNG and JPEG support, respectively. These libraries have certain limitations; for instance, NanoJPEG, and therefore rEFInd, does not support progressive JPEGs. If you have problems with a specific icon or banner image, check the libraries' pages and re-save or convert the image into a more generic form, or even to a different format.

      @@ -220,15 +231,15 @@
        -
      • Under Linux, the ESP is usually mounted at /boot/efi, although some users, particularly in Arch and Gentoo, prefer to mount the ESP at /boot.
      • +
      • Under Linux, the ESP is usually mounted at /boot/efi, although some users, particularly in Arch and Gentoo, prefer to mount the ESP at /boot, or sometimes at /efi.
      • Under macOS, the ESP is not mounted by default, so you must mount it yourself to access it. Since 0.9.3, rEFInd has provided a script called mountesp, which locates and mounts the ESP. Open a Terminal and type sudo mountesp to mount the ESP. The program should tell you where it's mounted the ESP. It will remain mounted until you manually unmount it or until you reboot.
      • -
      • Under Windows, the ESP is not mounted by default. You can do so manually by opening an Administrator Command Prompt window and typing mountvol S: /S to mount it at S:. (You can change the drive letter if you like.) Note that you will be able to access the ESP only from this Administrator Command Prompt window.
      • +
      • Under Windows, the ESP is not mounted by default. You can do so manually by opening an Administrator Command Prompt window and typing mountvol R: /S to mount it at R:. (You can change the drive letter if you like.) Note that you will be able to access the ESP only from this Administrator Command Prompt window.
      -

      As a further twist, on Macs rEFInd can exist on its own partition or on the main macOS partition, depending on the version of rEFInd you've installed and the options you passed to the installation script. rEFInd has installed to the ESP by default since version 0.8.4. rEFInd typically lives on the ESP in the EFI/refind directory, or sometimes in EFI/BOOT or elsewhere. Thus, the rEFInd configuration file might be /boot/efi/EFI/refind/refind.conf, /boot/EFI/BOOT/refind.conf, /Volumes/ESP/EFI/refind/refind.conf, S:\EFI\refind\refind.conf, or something else, depending on your OS and mount point.

      +

      As a further twist, on Macs rEFInd can exist on its own partition or on the main macOS partition, depending on the version of rEFInd you've installed and the options you passed to the installation script. rEFInd has installed to the ESP by default since version 0.8.4. rEFInd typically lives on the ESP in the EFI/refind directory, or sometimes in EFI/BOOT or elsewhere. Thus, the rEFInd configuration file might be /boot/efi/EFI/refind/refind.conf, /boot/EFI/BOOT/refind.conf, /Volumes/ESP/EFI/refind/refind.conf, R:\EFI\refind\refind.conf, or something else, depending on your OS and mount point.

      You can use any text editor you like to edit refind.conf, but be sure it saves the file in plain ASCII text, not in a word processing format. (In theory, a UTF-16 encoding should also work, but this has been poorly tested.) Note that the EFI shell includes its own editor. If you need to make a change before you launch an OS, you can launch a shell, change to the rEFInd directory, and type edit refind.conf to edit the file. This EFI editor is quite primitive, but it gets the job done. After editing, you'll need to reboot or re-launch rEFInd, or hit the Esc key, for rEFInd to read the changed configuration file.

      @@ -254,12 +265,17 @@ shutdown_after_timeout none or one of true, on, 1, false, off, or 0 - If set to false or one of its synonyms (the default), rEFInd boots the option set via default_selection when the timeout period is reached. When set to true or one of its synonyms, rEFInd attempts to shut down the computer when the timeout period is reached. Many EFIs lack software shutdown support. On them, setting this option to true will cause a reboot or a hang once the timeout value is reached! + If set to false or one of its synonyms (the default), rEFInd boots the option set via default_selection when the timeout period is reached. When set to true or one of its synonyms, rEFInd attempts to shut down the computer when the timeout period is reached. Many older EFIs lack software shutdown support. On them, setting this option to true will cause a reboot or a hang once the timeout value is reached! + + + log_level + numeric value (0 to 4) + Sets the logging level. The default level of 0 performs no logging. Higher values cause rEFInd to log information on its activity to the refind.log file, which resides in the directory from which rEFInd launched; or in the root of the first ESP that rEFInd can locate, if rEFInd's launch directory is read-only (as when rEFInd is launched from an HFS+ volume). The resulting log file is in UTF-16 format, so you'll need to read it with a text editor or other tool that can parse UTF-16. This feature is intended to help with debugging problems; the log level should be kept at 0 in normal operation. Increasing the log level, especially above 1, can degrade rEFInd's performance. use_nvram none or one of true, on, 1, false, off, or 0 - If set to true, on, or 1 (the default), stores rEFInd-specific variables in NVRAM. If set to false, off, or 0, stores these variables in the vars subdirectory of rEFInd's home directory, if possible. (Read-only filesystems obviously make this option impossible.) Using NVRAM ties rEFInd's variables to a specific computer and increases wear on the NVRAM, whereas storing them on disk makes the variables move with rEFInd (which is potentially useful on an installation on a removable disk). + If set to true, on, or 1, stores rEFInd-specific variables in NVRAM. If set to false, off, or 0, stores these variables in the vars subdirectory of rEFInd's home directory; in the refind-vars directory on the first ESP that rEFInd can identify, if rEFInd's own directory cannot be modified (for instance, if it's an HFS+ volume); or in NVRAM, if both disk storage options fail. Using NVRAM ties rEFInd's variables to a specific computer and increases wear on the NVRAM, whereas storing them on disk makes the variables move with rEFInd (which is potentially useful on a rEFInd installation on a removable disk). The internal default is true; however, use_nvram false is set in the default refind.conf file, so rEFInd will use disk storage by default on a fresh installation. This option has no effect on storage of non-rEFInd-specific variables, such as LoaderDevicePartUUID (if write_systemd_vars is enabled) or BootNext (used by the loaders that reboot to EFI-defined boot programs). screensaver @@ -269,7 +285,20 @@ hideui banner, label, singleuser, safemode, hwtest, arrows, hints, editor, badges, or all - Removes the specified user interface features. banner removes the banner graphic or background image, label removes the text description of each tag and the countdown timer, singleuser removes the single-user option from the macOS sub-menu, safemode removes the option to boot to safe mode from the macOS sub-menu, hwtest removes the Macintosh hardware test option, arrows removes the arrows to the right or left of the OS tags when rEFInd finds too many OSes to display simultaneously, hints removes the brief description of what basic keypresses do, editor disables the options editor, badges removes the device-type badges from the OS tags, and all removes all of these features. You can specify multiple parameters with this option. The default is to set none of these values. +

      Removes the specified user interface features:

      +
        +
      • banner removes the banner graphic or background image
      • +
      • label removes the text description of each tag and the countdown timer
      • +
      • singleuser removes the single-user option from the macOS sub-menu
      • +
      • safemode removes the option to boot to safe mode from the macOS sub-menu
      • +
      • hwtest removes the Macintosh hardware test option
      • +
      • arrows removes the arrows to the right or left of the OS tags when rEFInd finds too many OSes to display simultaneously
      • +
      • hints removes the brief description of what basic keypresses do
      • +
      • editor disables the options editor
      • +
      • badges removes the device-type badges from the OS tags
      • +
      • all removes all of these features.
      • +
      +

      You can specify multiple parameters with this option. The default is to set none of these values.

      icons_dir @@ -308,8 +337,29 @@ showtools - shell, memtest, gdisk, gptsync, install, apple_recovery, csr_rotate, mok_tool, fwupdate, netboot, about, hidden_tags, 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, install installs rEFInd from the booted medium to another ESP, apple_recovery boots the macOS Recovery HD, csr_rotate rotates through System Integrity Protection (SIP) values specified by csr_values, windows_recovery boots a Windows recovery tool, mok_tool launches a tool to manage Machine Owner Keys (MOKs) on systems with Secure Boot active, fwupdate launches a firmware-update tool, netboot launches the network boot tool (iPXE), about displays information about rEFInd, hidden_tags enables you to recover tags you've hidden 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, gdisk, apple_recovery, mok_tool, and fwupdate 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, is not installed by default except under macOS. See the "Installing Additional Components" section of the Installing rEFInd page for pointers to the shell, Memtest86, gptsync, and gdisk 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, install, bootorder, apple_recovery, csr_rotate, mok_tool, fwupdate, netboot, about, hidden_tags, exit, shutdown, reboot, and firmware +

      Specifies which tool tags to display on the second row:

      +
        +
      • shell launches an EFI shell, if one is found on the disk or available as a built-in EFI feature
      • +
      • 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
      • +
      • install installs rEFInd from the booted medium to another ESP
      • +
      • bootorder provides a tool for manipulating the EFI boot order (not the order of items in rEFInd's own menu)
      • +
      • apple_recovery boots the macOS Recovery HD
      • +
      • csr_rotate rotates through System Integrity Protection (SIP) values specified by csr_values
      • +
      • windows_recovery boots a Windows recovery tool
      • +
      • mok_tool launches a tool to manage Machine Owner Keys (MOKs) on systems with Secure Boot active
      • +
      • fwupdate launches a firmware-update tool
      • +
      • netboot launches the network boot tool (iPXE)
      • +
      • about displays information about rEFInd
      • +
      • hidden_tags enables you to recover tags you've hidden
      • +
      • exit terminates rEFInd
      • +
      • shutdown shuts down the computer (or reboots it, on some UEFI PCs)
      • +
      • reboot reboots the computer
      • +
      • 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, windows_recovery, mok_tool, about, hidden_tags, shutdown, reboot, firmware, fwupdate. Note that the shell, memtest, gdisk, apple_recovery, mok_tool, and fwupdate 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, is not installed by default except under macOS. See the "Installing Additional Components" section of the Installing rEFInd page for pointers to the shell, Memtest86, gptsync, and gdisk 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 @@ -328,8 +378,8 @@ resolution - one or two integer values - Sets the video resolution used by rEFInd; takes either a width and a height or a single UEFI video mode number as options. For instance, resolution 1024 768 sets the resolution to 1024x768. On UEFI systems, resolution 1 sets video mode 1, the resolution of which varies from system to system. If you set a resolution that doesn't work on a UEFI-based system, rEFInd displays a message along with a list of valid modes. On a system built around EFI 1.x (such as a Mac), setting an incorrect resolution fails silently; you'll get the system's default resolution. You'll also get the system's default resolution if you set both resolution values to 0 or if you pass anything but one or two numbers. (Note that passing a resolution with an x, as in 1024x768, will be interpreted as one option and so will cause the default resolution to be used.) If you get a higher resolution than you request, try commenting out or changing the textmode value, since it can force the system to use a higher graphics resolution than you specify with resolution. Also, be aware that it is possible to set a valid resolution for your video card that's invalid for your monitor. If you do this, your monitor will go blank until you've booted an OS that resets the video mode. + one or two integer values; or the string max + Sets the graphical video resolution used by rEFInd; takes a width and a height or a single UEFI video mode number or the string max as options. For instance, resolution 1024 768 sets the resolution to 1024x768. On UEFI systems, resolution 1 sets video mode 1, the resolution of which varies from system to system. The value max sets the highest available resolution, which is often desirable, but with caveats described shortly. If you set a resolution that isn't valid on a UEFI-based system, rEFInd displays a message along with a list of valid modes. On a system built around EFI 1.x (such as a Mac), setting an incorrect resolution fails silently; you'll get the system's default resolution. You'll also get the system's default resolution if you set both resolution values to 0 or if you pass anything but one or two numbers. (Note that passing a resolution with an x, as in 1024x768, will be interpreted as one option and so will cause the default resolution to be used.) If you get a higher resolution than you request, try commenting out or changing the textmode value, since it can force the system to use a higher graphics resolution than you specify with resolution. Also, be aware that it is possible to set a valid resolution for your video card that's invalid for your monitor; this is a real risk with the max option. If you do this, your monitor will go blank until you've booted an OS that resets the video mode. enable_touch @@ -354,7 +404,7 @@ use_graphics_for osx, linux, elilo, grub, and windows - Ordinarily, rEFInd clears the screen and displays basic boot information when launching any OS but macOS. For macOS, the default behavior is to clear the screen to the default background color and display no information. You can specify the simpler Mac-style behavior by specifying the OSes or boot loaders you want to work this way with this option. (OSes that should use text-mode displays should be omitted from this list.) Note that this option doesn't affect what the boot loader does; it may display graphics, text, or nothing at all. Thus, the effect of this option is likely to last for just a fraction of a second. On at least one firmware (used on some Gigabyte boards), setting use_graphics_for linux is required to avoid a system hang when launching Linux via its EFI stub loader. To add to the default list, specify + as the first option, as in use_graphics_for + windows. + Ordinarily, rEFInd clears the screen and displays basic boot information when launching any OS but macOS. For macOS, the default behavior is to clear the screen to the default background color and display no information. You can specify the simpler Mac-style behavior by specifying the OSes or boot loaders you want to work this way with this option. (OSes that should use text-mode displays should be omitted from this list.) Note that this option doesn't affect what the follow-on boot loader does; it may display graphics, text, or nothing at all. Thus, the effect of this option is likely to last for just a fraction of a second. On at least one firmware (used on some Gigabyte boards), setting use_graphics_for linux is required to avoid a system hang when launching Linux via its EFI stub loader. To add to the default list, specify + as the first option, as in use_graphics_for + windows. scan_driver_dirs @@ -363,8 +413,16 @@ scanfor - 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. + internal, external, optical, netboot, hdbios, biosexternal, cd, manual, and firmware +

      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 and deprecated. 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.
      • +
      • The firmware option tells rEFInd to scan for boot options stored in the EFI's own boot order list; when such a tag is selected by the user, rEFInd sets the EFI BootNext variable and reboots, causing the selected option to run. The upcoming section, Using Firmware Boot Options, covers this topic in more detail.
      • +
      +

      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, although the order of multiple options within each group is determined by the order in which rEFInd discovers the specific loaders. The default is internal, external, optical, manual on most systems, but internal, hdbios, external, biosexternal, optical, cd, manual on Macs.

      uefi_deep_legacy_scan @@ -384,22 +442,22 @@ dont_scan_volumes or don't_scan_volumes filesystem or partition label(s) - Adds the specified volume or volumes to a volume "blacklist"—these filesystems are not scanned for EFI boot loaders. This may be useful to keep unwanted EFI boot entries, such as for an OS recovery partition, from appearing on the main list of boot loaders. You can identify a volume by its filesystem name, its GPT volume name, or by its GPT unique GUID value. The default value is LRS_ESP, to keep the Lenovo Windows recovery volume from appearing. (This volume should get its own tools icon instead—see the showtools token.) You can use dont_scan_volumes to hide disks or partitions from legacy-mode scans, too. In this case, you can enter any part of the description that appears beneath the icons to hide entries that include the string you specify. + Adds the specified volume or volumes to a volume "exclusion list"—these filesystems are not scanned for EFI boot loaders. This may be useful to keep unwanted EFI boot entries, such as for an OS recovery partition, from appearing on the main list of boot loaders. You can identify a volume by its filesystem name, its GPT volume name, or by its GPT unique GUID value. The default value is LRS_ESP, to keep the Lenovo Windows recovery volume from appearing. (This volume should get its own tools icon instead—see the showtools token.) You can use dont_scan_volumes to hide disks or partitions from legacy-mode scans, too. In this case, you can enter any part of the description that appears beneath the icons to hide entries that include the string you specify. dont_scan_dirs or don't_scan_dirs directory path(s) - Adds the specified directory or directories to a directory "blacklist"—these directories are not scanned for boot loaders. You may optionally precede a directory path with a volume name and a colon to limit the blacklist to that volume; otherwise all volumes are affected. For instance, EFI/BOOT prevents scanning the EFI/BOOT directory on all volumes, whereas ESP:EFI/BOOT blocks scans of EFI/BOOT on the volume called ESP but not on other volumes. You can use a filesystem unique GUID, as in 2C17D5ED-850D-4F76-BA31-47A561740082, in place of a volume name. This token may be useful to keep duplicate boot loaders out of the menu; or to keep drivers or utilities out of the boot menu, if you've stored them in a subdirectory of EFI. This option takes precedence over also_scan_dirs; if a directory appears in both lists, it will not be scanned. To add directories to the default list rather than replace the list, specify + as the first option, as in dont_scan_dirs + EFI/dontscan. The default for this token is EFI/tools, EFI/tools/memtest86, EFI/tools/memtest, EFI/memtest86, EFI/memtest, com.apple.recovery.boot. + Adds the specified directory or directories to a directory "exclusion list"—these directories are not scanned for boot loaders. You may optionally precede a directory path with a volume name and a colon to limit the exclusion list to that volume; otherwise all volumes are affected. For instance, EFI/BOOT prevents scanning the EFI/BOOT directory on all volumes, whereas ESP:EFI/BOOT blocks scans of EFI/BOOT on the volume called ESP but not on other volumes. You can use a filesystem unique GUID, as in 2C17D5ED-850D-4F76-BA31-47A561740082, in place of a volume name. This token may be useful to keep duplicate boot loaders out of the menu; or to keep drivers or utilities out of the boot menu, if you've stored them in a subdirectory of EFI. This option takes precedence over also_scan_dirs; if a directory appears in both lists, it will not be scanned. To add directories to the default list rather than replace the list, specify + as the first option, as in dont_scan_dirs + EFI/dontscan. The default for this token is EFI/tools, EFI/tools/memtest86, EFI/tools/memtest, EFI/memtest86, EFI/memtest, com.apple.recovery.boot. dont_scan_files or don't_scan_files filename(s) - Adds the specified filename or filenames to a filename "blacklist" for OS loaders—these files are not included as boot loader options even if they're found on the disk. This is useful to exclude support programs (such as shim.efi and MokManager.efi) and drivers from your OS list. The default value is shim.efi, shim-fedora.efi, shimx64.efi, PreLoader.efi, TextMode.efi, ebounce.efi, GraphicsConsole.efi, MokManager.efi, HashTool.efi, HashTool-signed.efi, fb{arch}.efi (where {arch} is the architecture code, such as x64). You can add a pathname and even a volume specification (filesystem name, partition name, or partition unique GUID), as in ESP:/EFI/BOOT/backup.efi, /boot/vmlinuz-bad, to block the boot loaders only in those specified locations. To add files to the default list rather than replace the list, specify + as the first option, as in dont_scan_files + badloader.efi. + Adds the specified filename or filenames to a filename "exclusion list" for OS loaders—these files are not included as boot loader options even if they're found on the disk. This is useful to exclude support programs (such as shim.efi and MokManager.efi) and drivers from your OS list. The default value is shim.efi, shim-fedora.efi, shimx64.efi, PreLoader.efi, TextMode.efi, ebounce.efi, GraphicsConsole.efi, MokManager.efi, HashTool.efi, HashTool-signed.efi, fb{arch}.efi (where {arch} is the architecture code, such as x64). You can add a pathname and even a volume specification (filesystem name, partition name, or partition unique GUID), as in ESP:/EFI/BOOT/backup.efi, /boot/vmlinuz-bad, to block the boot loaders only in those specified locations. To add files to the default list rather than replace the list, specify + as the first option, as in dont_scan_files + badloader.efi. dont_scan_tools or don't_scan_tools filename(s) - Adds the specified filename or filenames to a filename "blacklist" for tools—these files are not included as tools (second-line options) even if they're found on the disk and are specified to be included via showtools. This is useful to trim an overabundance of tools. For instance, if you install multiple Linux distributions, you may end up with multiple MokManager entries, but you'll need just one. You can add a pathname and even a volume specification (filesystem name, partition name, or partition unique GUID), as in ESP:/EFI/tools/shellx64.efi, EFI/ubuntu/mmx64.efi, to block the tools only in those specified locations. The default value is an empty list (nothing is excluded). + Adds the specified filename or filenames to a filename "exclusion list" for tools—these files are not included as tools (second-line options) even if they're found on the disk and are specified to be included via showtools. This is useful to trim an overabundance of tools. For instance, if you install multiple Linux distributions, you may end up with multiple MokManager entries, but you'll need just one. You can add a pathname and even a volume specification (filesystem name, partition name, or partition unique GUID), as in ESP:/EFI/tools/shellx64.efi, EFI/ubuntu/mmx64.efi, to block the tools only in those specified locations. The default value is an empty list (nothing is excluded). windows_recovery_files @@ -414,7 +472,7 @@ fold_linux_kernels none or one of true, on, 1, false, off, or 0 - When uncommented or set to true, on, or 1, causes rEFInd to "fold" all Linux kernels in a given directory into a single main-menu icon. Selecting that icon launches the most recent kernel. To launch an older kernel, you must press F2, Insert, or Tab; older kernels appear on the resulting submenu display. (You can type, as root, touch /boot/vmlinuz-{whatever}, to make /boot/vmlinuz-{whatever} your default kernel in a directory.) If you prefer to see all your kernels in the main menu, set this option to false, off, or 0. Note that this option is new with version 0.9.0, which changes the default behavior; earlier versions of rEFInd behaved as if fold_linux_kernels false was set. + When uncommented or set to true, on, or 1 (the default), causes rEFInd to "fold" all Linux kernels in a given directory into a single main-menu icon. Selecting that icon launches the most recent kernel. To launch an older kernel, you must press F2, Insert, or Tab; older kernels appear on the resulting submenu display. (You can type, as root, touch /boot/vmlinuz-{whatever}, to make /boot/vmlinuz-{whatever} your default kernel in a directory.) If you prefer to see all your kernels in the main menu, set this option to false, off, or 0. Note that this option is new with version 0.9.0, which changes the default behavior; earlier versions of rEFInd behaved as if fold_linux_kernels false was set. extra_kernel_version_strings @@ -422,6 +480,11 @@ For the benefit of Linux distributions, such as Arch, that lack version numbers in their kernel filenames but that can provide multiple kernels, you can specify strings that can treated like version numbers. For instance, for Arch you might set this to linux-lts,linux; thereafter, the vmlinuz-linux-lts kernel will match to an initrd file containing the string linux-lts and vmlinuz-linux will match an initrd file with a filename that includes linux, but not linux-lts. Note that, if one specified string is a subset of the other (as in this example), the longer substring must appear first in the list. Also, if a filename includes both a specified string and one or more digits, the match covers both; for instance, vmlinuz-linux-4.8 would match an initrd file with a name that includes linux-4.8. The default is to do no extra matching. + write_systemd_vars + none or one of true, on, 1, false, off, or 0 + When activated, and when rEFInd believes it is launching an EFI-stub Linux kernel, ELILO, or GRUB, rEFInd will write the GUID of the partition from which it was launched to the LoaderDevicePartUUID EFI variable with a vendor GUID of 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f, as described on the systemd Boot Loader Interface page. This has the effect of communicating to Linux's systemd what ESP is active; systemd will then mount the ESP at /boot or /efi if that directory is is empty and nothing else is mounted there. (Note that most Linux distributions use other means to mount the ESP at /boot/efi.) This option has no effect when launching non-Linux OSes (except via GRUB). It is disabled by default. Note that, because the variable is persistent, disabling this feature after booting with it enabled will not eliminate an existing entry. If this causes problems (say, if you want to keep your ESP unmounted by default but find that systemd keeps mounting it), you must alter the systemd configuration, as described in its documentation; or remove the relevant EFI variable. The latter can be done by typing chattr -i /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f as root, followed by rm /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f, also as root. + + max_tags numeric (integer) value Limits the number of tags that rEFInd will display at one time. If rEFInd discovers more loaders than this value, they're shown in a scrolling list. The default value is 0, which imposes no limit. @@ -432,7 +495,7 @@ Sets the default boot OS based on the loader's title, which appears in the main menu beneath the icons when you select the loader. This token takes one or three variables. The first variable is a set of one or more identifiers. If you specify more than one or if the identifier contains a space, it must be in quotation marks. If more than one identifier is present, they must be specified as a comma-separated list, all within a single set of quotation marks. For instance, default_selection "alpha,beta" will launch alpha if it's available, and beta if alpha is not available but beta is. Each identifier can be any one of three things:
      • The symbol +, which refers to the previously-launched boot entry. rEFInd stores (in NVRAM) the name of a boot entry before launching it, and effectively substitutes that stored string for the + in the default_selection line the next time rEFInd launches, then matches it as a string, as described next....
      • -
      • Any string, which is matched against boot descriptions. Note that rEFInd matches substrings, so you don't need to specify the complete description string, just a unique substring. Thus, default_selection vmlinuz matches vmlinuz, boot\vmlinuz-5.3.0-22-generic, or any other string that includes vmlinuz. rEFInd stops searching when it finds the first match. Because rEFInd sorts entries within a directory in descending order by file modification time, if you specify a directory (or volume name, for loaders in a partition's root directory) as the default_selection, the newest loader in that directory will be the default. As a special case, one-character strings are matched against the first character of the description, except for digits.
      • +
      • Any string, which is matched against boot descriptions. Note that rEFInd matches substrings, so you don't need to specify the complete description string, just a unique substring. Thus, default_selection vmlinuz matches vmlinuz, boot\vmlinuz-5.8.0-22-generic, or any other string that includes vmlinuz. rEFInd stops searching when it finds the first match. Because rEFInd sorts entries within a directory in descending order by file modification time, if you specify a directory (or volume name, for loaders in a partition's root directory) as the default_selection, the newest loader in that directory will be the default. As a special case, one-character strings are matched against the first character of the description, except for digits.
      • A digit (1 to 9), in which case the boot loader at that position in the boot list is launched. For instance, default_selection 2 makes the second boot entry the default.
      @@ -451,7 +514,7 @@ csr_values List of hexadecimal values - Specifies values that may be set via the csr_rotate tool for Apple's System Integrity Protection (SIP). SIP stores values in NVRAM to set restrictions on what users (even root) may do in macOS 10.11. If you want to be able to control these restrictions in rEFInd, you must set the values you want to use here and set csr_rotate on the showtools line (which must also be uncommented). Note that values are specified in hexadecimal, with no leading 0x or other hexadecimal indicator. SIP is described in more detail on many Web sites, such as here. + Specifies values that may be set via the csr_rotate tool for Apple's System Integrity Protection (SIP). SIP stores values in NVRAM to set restrictions on what users (even root) may do in macOS 10.11 and later. If you want to be able to control these restrictions in rEFInd, you must set the values you want to use here and set csr_rotate on the showtools line (which must also be uncommented). Note that values are specified in hexadecimal, with no leading 0x or other hexadecimal indicator. SIP is described in more detail on many Web sites, such as here. include @@ -468,10 +531,10 @@ banner custom.bmp scan_driver_dirs drivers,EFI/tools/drivers scanfor manual,external,optical -default_selection elilo +default_selection grub -

      This example sets a timeout of 5 seconds; loads a custom graphic file called custom.bmp from the directory in which the rEFInd binary resides; scans the drivers and EFI/tools/drivers directories for EFI drivers; uses manual boot loader configuration but also scans for external EFI boot loaders and EFI boot loaders on optical discs; and sets the default boot loader to the first loader found that includes the string elilo. Of course, since this file specifies use of manual boot loader configuration, it's not complete; you'll need to add at least one OS stanza to be able to boot from anything but an external disk or optical drive, as described shortly.

      +

      This example sets a timeout of 5 seconds; loads a custom graphic file called custom.bmp from the directory in which the rEFInd binary resides; scans the drivers and EFI/tools/drivers directories for EFI drivers; uses manual boot loader configuration but also scans for external EFI boot loaders and EFI boot loaders on optical discs; and sets the default boot loader to the first loader found that includes the string grub. Of course, since this file specifies use of manual boot loader configuration, it's not complete; you'll need to add at least one OS stanza to be able to boot from anything but an external disk or optical drive, as described shortly.

      Creating Manual Boot Stanzas

      @@ -479,7 +542,7 @@
      -

      Manual boot stanzas in rEFInd are similar to those in GRUB Legacy, GRUB 2, or ELILO. You can use them to add EFI boot loaders to those that are auto-detected. rEFInd does not yet support manual boot stanzas for BIOS-mode boot loaders. You also cannot modify the auto-detected options; if you just want to tweak one OS's configuration, you have several options:

      +

      Manual boot stanzas in rEFInd are similar to those in GRUB Legacy, GRUB 2, or ELILO. You can use them to add EFI boot loaders or firmware boot redirection to those that are auto-detected. rEFInd does not yet support manual boot stanzas for BIOS-mode boot loaders, although if an EFI creates its own NVRAM boot entry for a BIOS-mode boot option, a firmware redirection manual boot stanza might do the trick. You also cannot modify the auto-detected options using a manual boot stanza; if you just want to tweak one OS's configuration, you have several options:

        @@ -519,7 +582,11 @@ initrd filename - Sets the filename for a Linux kernel's initial RAM disk (initrd). This option is useful only when booting a Linux kernel that includes an EFI stub loader, which enables you to boot a kernel without the benefit of a separate boot loader. When booted in this way, though, you must normally pass an initrd filename to the boot loader. You must specify the complete EFI path to the initrd file with this option, as in initrd EFI/linux/initrd-3.3.0-rc7.img. You'll also have to use the options line to pass the Linux root filesystem, and perhaps other options (as in options "root=/dev/sda4 ro"). The initial RAM disk file must reside on the same volume as the kernel. + Sets the filename for a Linux kernel's initial RAM disk (initrd). This option is useful only when booting a Linux kernel that includes an EFI stub loader, which enables you to boot a kernel without the benefit of a separate boot loader. When booted in this way, though, you must normally pass an initrd filename to the boot loader. You must specify the complete EFI path to the initrd file with this option, as in initrd EFI/linux/initrd-3.8.0.img. You'll also have to use the options line to pass the Linux root filesystem, and perhaps other options (as in options "root=/dev/sda4 ro"). The initial RAM disk file must reside on the same volume as the kernel. + + firmware_bootnum + 16-bit hexadecimal number (0000 through FFFF + Sets the EFI boot loader's boot number to be executed. These numbers appear in the output of Linux's efibootmgr and in rEFInd's own EFI boot order list (when showtools bootorder is activated) as Boot#### values, for instance. When this option is used, most other tokens have no effect. In particular, this option is incompatible with volume, loader, initrd, options, and most other tokens. Exceptions are menuentry, icon, and disabled; these three tokens work with firmware_bootnum (and of course menuentry is required). The upcoming section, Using Firmware Boot Options, describes this boot method in more detail. icon @@ -688,6 +755,95 @@
      + +

      Using Firmware Boot Options

      +
      + +

      rEFInd's ability to reboot into a boot program specified via the EFI's own boot manager, introduced with rEFInd 0.13.0, is unusual and requires special elaboration. This option can be used in any of three ways:

      + +
        + +
      • Auto-scanning — One method is to set firmware as an option to the scanfor line. This method causes rEFInd to display most or all of the available EFI boot entries (as revealed by Linux's efibootmgr, among other methods) as first-line boot options. (One exception is described shortly, and in some cases some options may be omitted.)
      • + +
      • Manual boot stanzas — The firmware_bootnum token in a manual boot stanza creates an entry that launches the specified Boot#### entry. This token takes a single hexadecimal number as an option, so you can copy the number specified by efibootmgr to create an entry. This is a good way to use this feature if you want to access just one boot option in this way.
      • + +
      • Built-in EFI shells — If an option contains the string shell (case-insensitive) in its description, it is added to the list of available EFI shell programs and is displayed on the second line of the rEFInd options list, but is omitted from the first line of OS loaders. This second-row option is created only if shell is an option to showtools, and whether or not you've set firmware as an option to scanfor. Note that most desktop and laptop computers lack a built-in EFI shell, although they do exist on a few. This feature is more common on server computers.
      • + +
      + + The firmware boot option badge identifies boot options that cause
+    rEFInd to reboot the computer + +

      In the rEFInd menu, entries launched in this way are identified by a computer chip badge, as shown to the right (applied there to a macOS loader icon). Their descriptions (which appear when they're highlighted) also begin with the string "Reboot to" (the "Reboot to Computer Setup Utility" second-row option also contains this string, and works in a way that's similar, but not identical, to the firmware reboot options).

      + +

      No matter how they're created, these options work by setting the EFI's BootNext variable to point to the specified boot option and then reboot the computer. In theory, the effect is identical to setting this variable using the -n option to efibootmgr in Linux and then rebooting. There are several reasons you might want to use this option:

      + +
        + +
      • Overcoming HiDPI limitations in macOS — On some Macs with HiDPI (aka "Retina") displays, booting macOS from rEFInd 0.12.0 and earlier or using other methods with later versions, produces a lower-than-optimal resolution. For instance, my own iMac, when booted directly, produces a 4096x2304 display; but when booted via rEFInd's normal methods, the display is only 3360x1890 with macOS 10.x (but 3840x2160 with macOS 11). When using this firmware boot method, the full resolution becomes available.
      • + +
      • Overcoming other macOS problems — Apple has a history of changing its boot loader location and otherwise making changes that cause problems for rEFInd. In theory, this method of booting should be more robust against such changes.
      • + +
      • Booting over the network — Most computers have options to boot over the network using the Pre-Execution Environment (PXE) or the Hypertext Transfer Protocol (HTTP). Although rEFInd has long supported an experimental PXE-boot implementation that relies on a third-party binary called iPXE, this method is limited and has seen little development since it was introduced. Redirecting to a network boot method implemented in the firmware is likely to be more reliable; however, at the time of introducing this feature, this aspect of it has been given limited testing.
      • + +
      • Booting built-in EFI shells — As noted earlier, firmware-mediated boot methods enable rEFInd to launch built-in EFI shells, which can be more convenient than installing EFI shell binaries yourself. Also, whatever shell is provided by your EFI should be compatible with the EFI. Random EFI shell binaries downloaded off the Internet might or might not be compatible.
      • + +
      • Accessing obscure EFI-specific utilities — Some EFIs provide EFI boot order options to access the firmware setup utility, hardware RAID card configuration tools, and other obscure utilities. Making these tools available via rEFInd can be helpful.
      • + +
      • Booting strange OSes — Although rEFInd usually does a good job of auto-detecting and launching boot loaders, some oddball configurations might give it problems. (The macOS issues noted earlier are examples of this.) If you run into such problems, and if existing EFI boot entries can launch your OS, then this rEFInd feature may be a good way to get things working. Creating a more conventional manual boot stanza might also work in some cases.
      • + +
      • Secure Boot issues — Sometimes, a boot chain from one boot program to another to another can wreak havoc with Secure Boot checks. Rebooting from rEFInd into another boot chain erases the effects of rEFInd and whatever Shim it uses, which may help with such issues.
      • + +
      + +

      To be sure, in most cases this new rEFInd feature will provide minimal or no benefits. For this reason, and because the number of useless extra options detected can be quite high, the firmware option to scanfor is disabled by default. If you try this feature, you may run into several other limitations:

      + +
        + +
      • Speed — Because this boot method employs a reboot, extra time is added to the boot process. For instance, booting macOS normally on my iMac takes 13 seconds from the moment I press Enter on the macOS option in rEFInd to the time that the macOS login appears. Booting via the firmware reboot feature takes 26 seconds for the same measure.
      • + +
      • Mac HiDPI video benefits apply only to macOS — I have not tested every OS or boot method, but in my limited tests so far, Linux booted via a direct EFI stub entry did not gain the video resolution benefits noted above for macOS. I don't have Windows installed on this computer, so I can't be sure how it would react; but it's my understanding that Windows is affected even when booted via Apple's own boot manager, so I suspect Windows would not be helped, either.
      • + +
      • Lost rEFInd environment features — Because this method of booting employs a reboot, any environment options set by rEFInd will be lost. Such options include the video resolution, Shim (if rEFInd was launched via Shim), and any EFI drivers that rEFInd loaded. Thus, you won't be able to launch a Linux kernel stored on an ext4 filesystem using this method; and if you launch an EFI shell in this way, it won't be able to read filesystems handled by your EFI filesystem drivers, either.
      • + +
      • Some boot options don't work — For reasons I don't understand, some boot options simply don't work. This boot method seems to be completely useless on an ancient i386-based Mac Mini and on an "early 2014" MacBook Air 6,2, both of which run Apple's EFI 1.10. (My iMac with the HiDPI display has a UEFI 2.4 implementation, and it works with this feature.) In my testing, I've also seen other options randomly fail on various non-Apple computers.
      • + +
      • Boot options can change — EFIs and OSes can alter the EFI boot option list independently of the contents of the ESP. Thus, options can unexpectedly appear on or disappear from this list. You might see boot options appear if you insert a removable disk, for instance, even if you've disabled removable devices via a custom scanfor line. This is most likely to be an issue if you use the firmware option to scanfor, although a manual boot stanza definition might stop working if the Boot#### entry to which it refers were to change or disappear.
      • + +
      • Boot options can be misleading — EFI boot options can be mis-labeled. Of particular interest, if you installed rEFInd from macOS, chances are the boot option called Mac OS X and numbered Boot0080 will in fact launch rEFInd. This can make it impossible to launch macOS via this method. This particular problem can be overcome by some juggling of boot options: + +
          + +
        1. Prepare a USB flash drive or CD-R that holds rEFInd. (You can download images from the Getting rEFInd page.)
        2. + +
        3. Boot to macOS.
        4. + +
        5. Use Startup Disk under Control Panel to pick your macOS system for the next boot. This will replace the EFI's Boot0080 option that points to rEFInd with one that points to macOS.
        6. + +
        7. Reboot. MacOS should appear directly, bypassing rEFInd.
        8. + +
        9. Insert the rEFInd-containing USB flash drive or CD-R into your computer.
        10. + +
        11. Restart and hold down the Option (or Alt) key. You should see Apple's boot manager, showing an option to boot your rEFInd medium.
        12. + +
        13. Boot the rEFInd external medium.
        14. + +
        15. From here, you can either install rEFInd from rEFInd (although this approach has some caveats, as described in the Installing rEFInd from within rEFInd section of Installing and Uninstalling rEFInd; or boot to Linux and re-install rEFInd from there. (In fact, if you reboot to Linux, you need only create a new EFI boot entry for rEFInd; but it's likely to be easier to re-install it completely.)
        16. + +
        + + Similar problems can occur when installing rEFInd from Windows, if you + don't set the name of the boot loader; or in some other contexts. + There's likely to be less need to boot in this way in these situations, + though. + +
      • + +
      + +

      Overall, I don't recommend relying on this boot method in most cases; it's slow and may not be very reliable. In some cases, though, it can be very useful. This is definitely the case for HiDPI Macs, and may be the case if you need to occasionally boot from a network device. To avoid cluttering your display, you may want to create a manual boot stanza to support only the boot options you need. You can identify the boot number using efibootmgr in Linux or rEFInd's own boot order management tool. (This tool is disabled by default, but can be enabled by adding bootorder to the showtools line in refind.conf.) Alternatively, you can add firmware to the scanfor line and then hide the options you don't want to see using some of the methods described earlier, in Hiding and Displaying EFI Boot Loaders.

      +

      Adjusting the Default Boot Option

      @@ -702,15 +858,9 @@

      If you want to consistently boot a particular OS by default and ignore the previous boot, you can use default_selection, but omit the + at the start of the line.

      -

      - -

      - -

      -
      -

      copyright © 2012–2020 by Roderick W. Smith

      +

      copyright © 2012–2021 by Roderick W. Smith

      This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

      diff -Nru refind-0.12.0/docs/refind/drivers.html refind-0.13.2/docs/refind/drivers.html --- refind-0.12.0/docs/refind/drivers.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/drivers.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

      Originally written: 4/19/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

      +3/13/2021, referencing rEFInd 0.13.2

      This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

      @@ -146,16 +146,18 @@

      Although EFI implementations should be able to load drivers prior to rEFInd's launch, in my experience, most EFI implementations offer such poor control over EFI driver loading that they can't be counted on to do this. Thus, if you want to use EFI drivers, rEFInd's ability to do so can be useful. This page tells you why you might want to use drivers, how you can install and use rEFInd's own drivers, where you can go to find other drivers, and provides tips on a few specific drivers.

      + +
      -

      Why Should You Use EFI Drivers?

      +

      Why Should You Use EFI Drivers?

      EFI supports drivers, which can activate hardware or filesystems in the pre-boot environment. At the moment, EFI drivers are few and far between; but you can or might want to use them for various reasons:

        -
      • You can load a filesystem driver to gain access to files on a filesystem other than FAT (or HFS+ on Macs or ISO-9660 on some systems). This is most likely to be useful on a Linux installation, since a filesystem driver can enable you to store a Linux kernel with EFI stub loader or for use by ELILO on a Linux-native filesystem if your EFI System Partition (ESP) is getting crowded.
      • +
      • You can load a filesystem driver to gain access to files on a filesystem other than FAT (or HFS+ or APFS on Macs or ISO-9660 on some systems). This is most likely to be useful on a Linux installation, since a filesystem driver can enable you to store a Linux kernel with EFI stub loader or for use by ELILO on a Linux-native filesystem if your EFI System Partition (ESP) is getting crowded.
      • You can load a driver for a plug-in disk controller to give the EFI access to its disks. Note that this is not required if you place your boot loader (and perhaps your OS kernel) on another disk, or if the plug-in disk controller includes EFI-capable firmware. It could be handy, perhaps in conjunction with a filesystem driver, to enable the EFI to read a boot loader or kernel from a disk on a plug-in controller, though. I've received one report of the NVMe driver from Clover being useful to boot from an aftermarket NVMe disk on a Mac, for instance.
      • @@ -169,7 +171,7 @@

        Note that most of these uses are theoretical, at least to me; I don't know of any specific examples of EFI drivers (available as separate files) for most hardware, although the Clover boot loader project provides a few such drivers. I haven't tested these, though. Hardware drivers are often embedded in the firmware of the devices themselves, and should be loaded automatically by the EFI. Chances are good that a few such drivers are available, unknown to me, and more may become available in the future. If you happen to have a device and need support for it under EFI, searching for drivers is certainly worth doing.

        -

        To the best of my knowledge, the best reason to want EFI driver support in rEFInd is to provide access to filesystems. Although EFI filesystem driver choices are currently somewhat limited, those that are available can help to improve your installation and configuration options, particularly if you've found yourself "boxed in" by awkward installation or bugs, such as the dinky ESP that Ubuntu creates by default or the bug that prevents a Linux kernel with EFI stub loader support from booting from the ESP of at least some Macs.

        +

        To the best of my knowledge, the best reason to want EFI driver support in rEFInd is to provide access to filesystems. EFI filesystem drivers can help to improve your installation and configuration options, particularly if you've found yourself "boxed in" by awkward installation or bugs, such as a too-small ESP created by an OS installation or the bug that prevents a Linux kernel with EFI stub loader support from booting from the ESP of at least some Macs.

        As a side note, using an ISO-9660 driver can theoretically help you keep the size of a custom Linux boot CD/DVD down to a reasonable value. This is because EFI systems normally boot from optical discs by reading a FAT image file in El Torito format and treating that file as an ESP. If you need to store the kernel both in that file and directly in the ISO-9660 filesystem (to maintain bootability on BIOS systems), that can represent an unwanted extra space requirement. Placing rEFInd and an ISO-9660 driver in the FAT image file should enable you to store the kernel on the disc only once. Unfortunately, this doesn't work in practice. When the ISO-9660 driver is loaded from the El Torito image, the driver discovers that the optical disc is in use and refuses to access it. It's possible to use EFI shell commands to give the ISO-9660 driver access to the shell device, but this causes the El Torito access to go away, which means that anything loaded from the El Torito image (such as rEFInd) is likely to malfunction. Also, some EFI implementations include ISO-9660 drivers, so you might not need a separate ISO-9660 driver if you're building a disc for a particular computer.

        @@ -197,7 +199,7 @@
      • On macOS, the ESP is not normally mounted, but the mountesp script that comes with rEFInd will mount it and identify the mount point.
      • -
      • Windows also does not normally mount the ESP, but it can be mounted from an Administrator command prompt window by typing mountvol S: /S. (You can change S: to another drive identifier, if you like.)
      • +
      • Windows also does not normally mount the ESP, but it can be mounted from an Administrator command prompt window by typing mountvol R: /S. (You can change R: to another drive identifier, if you like.)
      @@ -240,7 +242,10 @@ volumes. As of version 0.6.1, this driver supports the meta_bg feature, which can also be used on ext2fs and ext3fs. Thus, it can handle some ext2fs and ext3fs partitions that the ext2fs driver can't - handle. You can learn about your ext2/3/4 filesystem features by typing + handle. Beginning with rEFInd 0.13.0, this driver supports activation of + the filesystem-level encryption code, but can not read encrypted + files. (The OS can encrypt directories that rEFInd doesn't read, + though.) You can learn about your ext2/3/4 filesystem features by typing dumpe2fs /dev/sda2 | grep features, changing /dev/sda2 to your filesystem's device.
    7. @@ -308,7 +313,7 @@ providing the driver mainly because it compiled cleanly with no extra work, aside from providing a Makefile entry for it. - +
    8. NTFS—Samuel Liao contributed this driver. As of rEFInd 0.11.5, this driver is not built by default; see the Warning sidebar to @@ -345,17 +350,17 @@
        -
      • Pete Batard's efifs drivers—This project is an EFI driver wrapper around GRUB 2's filesystem drivers. Once compiled, the result is that GRUB 2's drivers become standalone EFI filesystem drivers, loadable independently or by rEFInd. (rEFInd version 0.8.3 or later is required.) At present (driver version 1.3; February, 2020), several drivers, including NTFS, exFAT, ext2fs, ReiserFS, Btrfs, JFS, and XFS, are usable. The last time I tested them (version 0.7), some drivers were slow, and they hung on some computers, such as one of my Macs. They may have improved since then, and are likely to improve more in the future. Note that the ext2fs driver from this set works with ext3fs and ext4fs, too. In addition to the main link, you can check the github repository for the source code.
      • +
      • Pete Batard's efifs drivers—This project is an EFI driver wrapper around GRUB 2's filesystem drivers. Once compiled, the result is that GRUB 2's drivers become standalone EFI filesystem drivers, loadable independently or by rEFInd. (rEFInd version 0.8.3 or later is required.) The last time I checked (driver version 1.3; February, 2020), several drivers, including NTFS, exFAT, ext2fs, ReiserFS, Btrfs, JFS, and XFS, are usable. The previous time I tested them (version 0.7), some drivers were slow, and they hung on some computers, such as one of my Macs. They may have improved since then, and are likely to improve more in the future. Note that the ext2fs driver from this set works with ext3fs and ext4fs, too. In addition to the main link, you can check the github repository for the source code.
      • -
      • The LKL driver project—I have yet to look closely at this project, but I think it's a porting of the Linux kernel's filesystem drivers to EFI. The developer stated on the EFI developers' mailing list that they aren't yet stable, as of November 2016.
      • +
      • The LKL driver project—I have yet to look closely at this project, but I think it's a porting of the Linux kernel's filesystem drivers to EFI. The developer stated on the EFI developers' mailing list that they aren't yet stable, as of November 2016, and as of February of 2021, there appears to have been no development in several years.
      • rEFIt's ext2fs and ReiserFS drivers—You can gain read-only access to ext2fs, ext3fs, and ReiserFS volumes with these drivers, originally written by Christoph Pfisterer. You can use the binaries in the refit-bin-0.14/efi/tools/drivers directory of the binary package directly on a Mac. On a UEFI-based PC, though, you'll need to break the Mac-style "fat" binary into its 32- and 64-bit components. You can use my thin program for this job. As a practical matter, there's no advantage to using these drivers over rEFInd's drivers, since the latter are updated versions of the former.
      • -
      • Clover EFI's ISO-9660, ext2fs, ext4fs, and HFS+ drivers—This project is an offshoot of Tianocore, the main UEFI project. It's primarily a Hackintosh boot loader, but it includes drivers for ISO-9660, ext2fs, ext4fs, and HFS+; however, building them requires a fair amount of expertise. These drivers are closely related to rEFInd's own drivers; however, Clover has a large number of developers working on it, and some of its drivers (especially the HFS+ driver) have diverged significantly from rEFInd's version.
      • +
      • Clover EFI's ISO-9660, ext2fs, ext4fs, and HFS+ drivers—This project is an offshoot of Tianocore, the main UEFI project, combined with a fork of rEFIt that's independent of rEFInd. Clover is primarily a Hackintosh boot loader, but it includes drivers for ISO-9660, ext2fs, ext4fs, and HFS+; however, building them requires a fair amount of expertise. These drivers are closely related to rEFInd's own drivers; however, Clover has a large number of developers working on it, and some of its drivers (especially the HFS+ driver) have diverged significantly from rEFInd's version.
      • VirtualBox's HFS+ and ISO-9660 drivers—These drivers are available in source code form, and come with VirtualBox binaries. I've not attempted to compile them myself, but I've seen a report that suggests they may include assumptions that require use of MinGW, a GCC-based compiler for Windows (and cross-compiler to build Windows executables under Linux). I don't know of a source for binaries suitable for use on EFI-based computers; if you want to use them, you'll need to figure out how to compile them yourself. As noted earlier, rEFInd's drivers are closely related to these.
      • -
      • Ext2Pkg—This driver, which hasn't been updated since 2011, appears to be an ext2fs/ext3fs driver built independently of the driver written by Christoph Pfisterer. The linked-to sites provide access to source code via git but do not provide binaries. When I built binaries, they failed to work. Under VirtualBox, the driver loaded but then hung when I tried to access an ext2 filesystem. On a 32-bit Mac Mini, I got error messages when I tried to access an ext2 filesystem. As I write, the code was last updated in March of 2012. If you check the project and it's been updated more recently, it might be worth trying. Otherwise, I can't recommend this driver. I mention it here only in case it improves in the future.
      • +
      • Ext2Pkg—This driver, which hasn't been updated since 2012, appears to be an ext2fs/ext3fs driver built independently of the driver written by Christoph Pfisterer. The linked-to sites provide access to source code via git but do not provide binaries. When I built binaries, they failed to work. Under VirtualBox, the driver loaded but then hung when I tried to access an ext2 filesystem. On a 32-bit Mac Mini, I got error messages when I tried to access an ext2 filesystem. As I write, the code was last updated in March of 2012. If you check the project and it's been updated more recently, it might be worth trying. Otherwise, I can't recommend this driver. I mention it here only in case it improves in the future.
      • Paragon's UFSD—According to this 2013 blog post, Paragon Software has ported its Universal File System Drivers (UFSD) to EFI, providing "transparent access to NTFS, HFS+, ExFAT, and ExtFS" (sic). Neither the blog post nor the main UFSD page provides any download links, and it's unclear if the product is (or will be) available for free, on a pay basis, or even at all.
      • @@ -402,7 +407,7 @@
        -

        copyright © 2012–2020 by Roderick W. Smith

        +

        copyright © 2012–2021 by Roderick W. Smith

        This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

        diff -Nru refind-0.12.0/docs/refind/features.html refind-0.13.2/docs/refind/features.html --- refind-0.12.0/docs/refind/features.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/features.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

        Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

        +3/13/2021, referencing rEFInd 0.13.2

        This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

        @@ -123,138 +123,163 @@
        -

        rEFInd is a fork of the rEFIt boot manager. As such, it has many features in common with rEFIt. These include the following:

        +

        rEFInd is a fork of the rEFIt boot manager. As such, it has many features in common with rEFIt; however, rEFInd expands on rEFIt's capabilities. In particular, rEFInd makes it practical to directly launch Linux kernels; adds video resolution options; adds support for Secure Boot; provides features to launch BIOS/CSM/legacy-mode OSes on UEFI-based PCs (which rEFIt can do only on Macs); can be installed and used on newer versions of macOS (but the macOS installer is more primitive than the one provided with rEFIt); can install to a hard disk from a boot of rEFInd from a removable disk; and more.

        + +

        In broad outline, rEFInd's features include the following:

          -
        • Support for both text-mode and graphical operation.
        • +
        • Platform support -
        • Auto-detection of EFI and BIOS boot loaders.
        • +
            -
          • User-configurable graphics and icons—you can set your own background, set new icons, and so on.
          • +
          • Binaries exist for x86 (aka IA32), x86-64 (aka X64 or AMD64), and ARM64 (aka AARCH64). The x86-64 binary is the best-tested of these, and the ARM64 binary is the most poorly-tested. (I've tested ARM only within a QEMU emulator.)
          • -
          • Launch EFI boot loaders.
          • +
          • Works on EFI 1.1 (mainly Apple Macs) and UEFI 2.x (most EFI-based PCs and newer Macs).
          • -
          • Launch legacy (BIOS) boot loaders on Macs. (rEFInd also supports legacy boots on some UEFI PCs; see below.)
          • +
          • Packages exist as .zip files (cross-platform), RPMs (for Fedora, OpenSUSE, and various other Linux distributions), Debian packages (for Debian, Ubuntu, and related distributions), and Ubuntu PPAs. See the Getting rEFInd page for details.
          • -
          • Launch options for an external EFI shell or disk partitioner. (See the Installing rEFInd section for information on how to obtain and install these components.)
          • +
          • Installation is best done via a script that runs in macOS and Linux. (Linux packages may run the install script automatically.) Manual installation procedures for Linux, macOS, and Windows exist, too. An option to install rEFInd from a rEFInd USB flash drive or optical disc also exists. See the Installing and Uninstalling rEFInd page for details.
          • -
          • Provide the gptsync utility for creating hybrid MBRs. Note that rEFInd's version of gptsync is significantly updated compared to rEFIt's. Also, this tool should be used only on Macs that dual-boot with a BIOS-based OS, such as Windows; or very rarely on other computers.
          • +
          • rEFInd provides an option to manage the EFI's own boot order. This feature is disabled by default; to enable it, add bootorder to the showtools option in refind.conf.
          • -
          • Set OS-specific boot options, such as to launch macOS with verbose text-mode debug messages.
          • +
          • The main development platform is Linux, using either GNU-EFI or TianoCore EDK2 build kits. See the BUILDING.txt file in the source code package for information on compiling rEFInd yourself.
          • -
          • Load EFI drivers for filesystems or hardware devices not supported natively by your firmware. (This feature is absent in some builds of rEFIt and in rEFInd prior to version 0.2.7.)
          • +
          • rEFInd is licensed under the terms of the GNU General Pulic License (GPL), version 3.
          • -
          • Inclusion of drivers for the Linux ReiserFS and ext2 filesystems in the - main package. (These drivers are absent from rEFInd prior to version - 0.4.0. See below concerning drivers for additional filesystems.)
          • +
          -
        +
      • Automatic OS detection -

        rEFInd expands on rEFIt by providing features that improve on or go beyond those of rEFIt, such as:

        +
          -
            +
          • Rather than rely on a configuration file to identify bootable OSes, as is the case with most boot managers, rEFInd actively scans for EFI boot loader files, BIOS/CSM/legacy boot loaders, and EFI boot options stored in the EFI's own NVRAM-based boot manager memory. You can also manually identify boot options for EFI-based and NVRAM-defined boot loaders, similar to the way other boot managers work.
          • -
          • Bug fixes, focusing on those that have bothered me personally.
          • +
          • Although rEFInd is, first and foremost, a tool for launching EFI-based OSes, rEFInd is one of the few EFI boot programs that can redirect the boot process to BIOS-based OSes.
          • -
          • The ability to specify a configuration file to use at program launch - time via the -c filename - command-line option.
          • +
          • rEFInd can detect Linux kernels with EFI stub loader support to boot them directly, minimizing Linux configuration requirements. (See the upcoming bullet point on Linux-specific features for more details.)
          • -
          • User-configurable methods of detecting boot loaders: +
          • As part of its OS detection, rEFInd can identify several tools, including EFI shells, disk partitioning utilities, Windows and macOS recovery partitions, Secure Boot utilities, and memory test tools.
          • -
              -
            • Auto-detection of EFI boot loaders, independently on internal hard disks, external hard disks, optical discs, and network boot loaders.
            • -
            • Auto-detection of legacy BIOS boot loaders, independently on internal hard disks, external hard disks, and optical discs.
            • -
            • Manually via the configuration file
            • -
            +
          • You can boot to a network (PXE or HTTP) boot server in either of two ways: +
              +
            • Beginning with rEFInd 0.8.4, experimental network boot loader support exists via the iPXE EFI binaries. When activated, rEFInd should add a network-boot option to its menu when a suitable network boot server is available.
            • +
            • Beginning with rEFInd 0.13.0, the firmware reboot feature enables use of network boot options provided by the computer's firmware.
            • +
            - You can select which of these methods to use to construct the rEFInd main boot menu. Although rEFIt supports auto-detection, it does not support manual configuration; and rEFIt's options to enable, disable, and prioritize individual boot loader detection methods are primitive compared to those in rEFInd.
          • +
          • You can tell rEFInd to not scan certain volumes, directories, or filenames, to limit options you want to omit. This can be done various ways, as described here. The easiest method involves a few keypresses at rEFInd's main menu.
          • -
          • Beginning with rEFInd 0.8.4, experimental network boot loader support via the iPXE EFI binaries. When activated, rEFInd should add a network-boot option to its menu when a suitable network boot server is available.
          • +
          • You can specify additional directories to scan for boot loaders and drivers, beyond those scanned by default.
          • -
          • Support for launching legacy BIOS boot loaders on UEFI PCs with -suitable CSM support (as of version 0.4.6, with significant improvements in -version 0.8.0). Note that some UEFI PCs, -such as those with Gigabyte's Hybrid EFI, lack a usable CSM.
          • +
          • Pressing the Esc key causes rEFInd to re-scan boot loaders, to assist when changing removable media or after making a change to the configuration file with an EFI shell.
          • -
          • Improved flexibility in setting the default OS to boot. rEFInd enables specifying the default by any substring in the description. You can also specify multiple defaults, so that if the first isn't available, another will take its place (which is useful when using removable disks). You can also add time specifications to set a default to be used only during certain hours of the day. If no default loader is set, rEFInd defaults to the last-booted loader.
          • +
          • The delay before scanning for boot loaders upon program start can be adjusted, for systems on which there's a delay before disks become available.
          • -
          • Support for partition names or GUID values as fallbacks for filesystem labels in certain configuration file settings. Partition names may be shown as values to be displayed as part of the descriptive text for boot tags on the main menu, too, if a filesystem has no label.
          • +
          -
        • The ability to fine-tune options passed to EFI boot loaders, via manual configuration.
        • +
        • OS launch options -
        • An option editor to enable you to edit the options passed to an EFI boot loader on a per-boot basis.
        • +
            -
          • The ability to specify additional directories to scan for boot loaders and drivers (as of version 0.2.7).
          • +
          • OS launch options can be specified in the main rEFInd configuration file (refind.conf, stored in rEFInd's main directory); or for Linux kernels, in the refind_linux.conf file, stored in the same directory as the kernel.
          • -
          • The ability to specify volumes and directories to not be scanned for boot loaders, even if they would ordinarily be scanned (as of version 0.6.0 for volumes and 0.4.2 for directories).
          • +
          • You can set OS-specific boot options, such as to launch macOS with verbose text-mode debug messages.
          • -
          • The ability to specify boot loader files to be ignored. This can be set either in the configuration file or dynamically as you're using rEFInd.
          • +
          • rEFInd enables specifying the default boot option by any substring in the description. You can also specify multiple defaults, so that if the first isn't available, another will take its place (which is useful when using removable disks). You can also add time specifications to set a default to be used only during certain hours of the day. If no default loader is set, rEFInd defaults to the last-booted loader.
          • -
          • The ability to re-scan boot loaders, to assist when changing removable media or after making a change to the configuration file with an EFI shell (as of version 0.3.5).
          • +
          • You can fine-tune options passed to EFI boot loaders, via manual configuration.
          • -
          • A configurable delay before scanning for boot loaders, for systems on which there's a delay before disks become available (as of version 0.4.6).
          • +
          • An option editor enables you to edit the options passed to an EFI boot loader on a per-boot basis.
          • -
          • The ability to specify an additional icon storage directory, to assist in efforts to customize rEFInd's appearance (as of version 0.3.4).
          • +
          -
        • Support for icons, selection backgrounds, and banner graphics in PNG and JPEG formats, in addition to the ICNS and BMP formats supported by rEFIt.
        • +
        • Graphics features -
        • Support for full-screen banner images.
        • +
            -
          • Support for scaling icons, to adjust icon size for users with high-resolution displays, poor eyesight, or simply for personal preference reasons.
          • +
          • rEFInd support for both text-mode and graphical operation.
          • -
          • The ability to set the screen's graphics resolution, within limits imposed by the EFI (as of rEFInd 0.3.0). Similarly, as of version 0.6.0, you can specify the text-mode resolution.
          • +
          • You can set the screen's graphics resolution, within limits imposed by the EFI. Similarly, you can specify the text-mode resolution.
          • -
          • Proper handling of more OS options than can fit on the screen. (rEFIt displays an empty list in graphical mode when it detects too many OSes.)
          • +
          • User-configurable graphics, icons, and fonts exist—you can set your own background, set new icons, and so on.
          • -
          • The ability to handle some (but not all) touch-screen displays, as used on tablet computers.
          • +
          • rEFInd upports icons, selection backgrounds, and banner graphics in PNG, JPEG, ICNS ,and BMP formats, although some formats make more sense for some purposes than others. See Theming rEFInd for details.
          • -
          • On some (but not all) computers, the ability to handle mouse input to select the boot loader to run.
          • +
          • Icons, fonts, and banner backgrounds can be scaled to adjust icon size for users with high-resolution displays, poor eyesight, or simply for personal preference reasons.
          • -
          • Additional OS icons (most of which are Linux distributions, at least so far). This can make it easier to find a specific distribution in the boot list if you've installed multiple Linux distributions.
          • +
          • A screen saver feature, activated by the screensaver seconds token in refind.conf, exists: Set seconds to the number of seconds before the screen will blank to prevent burn-in.
          • -
          • Support for loading user-defined fonts, in the form of PNG files containing ASCII characters 32 through 126 plus a glyph to be used for values outside that range.
          • +
          -
        • The ability to auto-detect Linux initial RAM disk files and to read Linux kernel options from a refind_linux.conf file. These features support (nearly) automatic handling of Linux kernels with embedded EFI stub loader support (a new feature with Linux 3.3.0).
        • +
        • User interaction features -
        • The ability to "fold" multiple Linux kernels into a single entry in the main menu. Additional kernels appear as options in the submenu. This feature is enabled by default, but can be disabled by setting fold_linux_kernels false in refind.conf.
        • +
            -
          • In the absence of a refind_linux.conf file, the ability to pass minimal Linux boot options to a kernel based on the contents of /etc/fstab. This is limited to cases in which the kernel resides on the Linux root (/) filesystem, though, and it won't work if the installation requires any unusual options.
          • +
          • Some (but not all) touch-screen displays, as used on tablet computers, work. (This feature is disabled by default; you must uncomment the enable_touch token in refind.conf to activate it.)
          • -
          • As of rEFInd 0.9.0, if a Linux root (/) filesystem is identified by the type code specified by the Discoverable Partitions Specification (DPS) and the root filesystem cannot be identified via refind_linux.conf or /etc/fstab, rEFInd passes a kernel root= identifier based on the identified DPS root (/) type code.
          • +
          • On some (but not all) computers, mouse input can be used to select the boot loader to run. (As with touch screens, this feature is disabled by default; you must uncomment enable_mouse to activate it. The mouse_size and mouse_speed tokens fine-tune mouse operation.)
          • -
          • Fixes to display problems on many UEFI-based PCs.
          • +
          -
        • Beginning with version 0.6.10, a screen saver feature, activated by the screensaver seconds token in refind.conf: Set seconds to the number of seconds before the screen will blank to prevent burn-in.
        • +
        • Mac-specific features -
        • Detection of a fallback boot loader (EFI/BOOT/bootx64.efi or EFI/BOOT/bootia32.efi) that's redundant with another boot loader, to keep the fallback boot loader out of menus when it's unnecessary.
        • +
            -
          • An "exit" option (disabled by default), so that you can return to whatever shell or boot manager you used to launch rEFInd, should this ability be desirable. (This feature first appeared in rEFInd 0.2.4.)
          • +
          • rEFInd can spoof the booting of macOS when booting non-Apple OSes. This changes the way a Mac's EFI initializes hardware, which can get secondary video chipsets working on some Macs. This feature is controlled via the spoof_osx_version token in refind.conf.
          • -
          • Drivers for ISO-9660, NTFS, HFS+, ext4fs, and Btrfs, which are not included in rEFIt. (The NTFS driver has proven troublesome, and so is not compiled by default.) The ISO-9660 driver is based on code from the rEFIt project, but was never completed by its original author. It was completed by Oracle for VirtualBox. The ext4fs driver is derived from the rEFIt ext2fs driver, and the Btrfs and NTFS drivers are derived from the rEFIt and GRUB 2.0 driver code.
          • +
          • rEFInd can adjust Apple System Integrity Protection (SIP; aka "rootless" or "CSR") settings. These settings control what features are off-limits even to root in macOS 10.11 (El Capitan) and later. To use this feature, you must set specific CSR values on refind.conf's csr_values line and add csr_rotate to the showtools line.
          • -
          • Beginning with version 0.5.0, the ability to "talk" to the shim boot loader to validate binaries supported by shim or its machine owner key (MOK) list when booting with Secure Boot active.
          • +
          -
        • The gptsync utility, included with rEFInd 0.6.9 and later, has safety checks to prevent creating a fresh hybrid MBR if the MBR side has been adjusted without adjusting the GPT side—a common source of problems. This update also prioritizes partition inclusion in the hybrid MBR, which can help on disks that have many partitions. OTOH, as of rEFInd 0.6.9, this version of gptsync is relatively untested.
        • + +
        • Linux-specific features + -
        • The ability to set the VMX bit on certain Intel CPUs. This feature is necessary for certain virtualization tools, such as Hyper-V, and not all EFIs enable users to set it.
        • +
            -
          • Beginning with version 0.10.0, the ability to spoof the booting of macOS when booting non-Apple OSes. This changes the way a Mac's EFI initializes hardware, which can get secondary video chipsets working on some Macs. This feature is controlled via the spoof_osx_version token in refind.conf.
          • +
          • rEFInd can auto-detect Linux initial RAM disk files and read Linux kernel options from a refind_linux.conf file. These features support (nearly) automatic handling of Linux kernels with embedded EFI stub loader support.
          • -
          • Beginning with version 0.10.0, the ability to adjust Apple System Integrity Protection (SIP; aka "rootless" or "CSR") settings. These settings control what features are off-limits even to root in macOS 10.11 (El Capitan) and later. To use this feature, you must set specific CSR values on refind.conf's csr_values line and add csr_rotate to the showtools line.
          • +
          • By default, rEFInd "folds" multiple Linux kernels into a single entry in the main menu. Additional kernels appear as options in the submenu. This feature can be disabled by setting fold_linux_kernels false in refind.conf, which causes each kernel to get its own main-menu icon.
          • -
          • Beginning with version 0.10.1, support for ARM64 (aka AARCH64 or AA64) CPUs. Such systems that boot using UEFI are quite rare, at least in December of 2015.
          • +
          • In the absence of a refind_linux.conf file, rEFInd tries to guess minimal Linux boot options for a kernel based on the contents of /etc/fstab and/or the partition type codes specified by the Discoverable Partitions Specification (DPS). These options are limited in their abilities, but they're often enough to get an installation to boot without any system-specific configuration.
          • -
          +
      • + +
      • Extra tools and utilities + +
          + +
        • rEFInd ships with EFI filesystem drivers for several common filesystems, including ext2/3fs, ext4fs, Btrfs, ReiserFS, HFS+, and ISO-9660. Code for an NTFS driver is also included, but is not compiled by default because the driver has proven to be troublesome. The main use for these drivers is to enable rEFInd to read Linux kernels from popular Linux-native filesystems; however, other uses exist, such as facilitating booting from some optical discs or making very large files available to other EFI utilities.
        • + +
        • The gptsync utility can create a fresh hybrid MBR on a GPT disk. Hybrid MBRs are ugly and dangerous, so gptsync is not installed by default except under macOS, and the rEFInd configuration file option to activate it is disabled by default. That said, hybrid MBRs were required to launch older BIOS-mode OSes, such as Windows 7 and earlier, on Intel-based Macs, and they are still useful when launching old or exotic OSes, mostly on Macs.
        • + +
      • + +
      • Miscellaneous features -

        On the flip side, at least for Mac users, rEFInd comes with less sophisticated Mac installation tools than does rEFIt, in favor of more OS-agnostic packaging.

        +
          + +
        • For debugging problems, the log_level token in refind.conf enables activating logging to a log file, refind.log, which is stored in rEFInd's main directory (or in the root of the first ESP that rEFInd can find, if rEFInd can't write to its own directory).
        • + +
        • You can specify a configuration file to use at program launch time via the -c filename command-line option. This feature requires manual installation or launching rEFInd from an EFI shell or some other EFI program you can configure.
        • + +
        • rEFInd detects a fallback boot loader (EFI/BOOT/bootx64.efi or EFI/BOOT/bootia32.efi) that's redundant with another boot loader, to keep the fallback boot loader out of menus when it's unnecessary.
        • + +
        • rEFInd provides an "exit" option (disabled by default), so that you can return to whatever shell or boot manager you used to launch rEFInd, should this ability be desirable.
        • + +
        • rEFInd can "talk" to the Shim boot loader to validate binaries supported by Shim or its machine owner key (MOK) list when booting with Secure Boot active.
        • + +
        • rEFInd can set the VMX bit on certain Intel CPUs. This feature is necessary for certain virtualization tools, such as Hyper-V, and not all EFIs enable users to set it.
        • + +
      • + +

      If these features sound useful, then read on and try rEFInd. If not, you may need to look elsewhere. My Managing EFI Boot Loaders for Linux page may be useful to you in this case.


      -

      copyright © 2012–2020 by Roderick W. Smith

      +

      copyright © 2012–2021 by Roderick W. Smith

      This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

      Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/docs/refind/firmware-badge.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/docs/refind/firmware-badge.png differ diff -Nru refind-0.12.0/docs/refind/getting.html refind-0.13.2/docs/refind/getting.html --- refind-0.12.0/docs/refind/getting.html 2020-03-13 12:47:59.000000000 +0000 +++ refind-0.13.2/docs/refind/getting.html 2021-03-14 00:03:44.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

      Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

      +3/13/2021, referencing rEFInd 0.13.2

      This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

      @@ -132,7 +132,7 @@
    9. Arch Linux—You can obtain rEFInd from the Arch - repositories, in both a stable version (the refind-efi package + repositories, in both a stable version (the refind package installable via pacman) and an experimental release built from rEFInd's git repository in the Arch User Repository (AUR), under the name refind-efi-git. The git release is likely to include @@ -329,7 +326,7 @@
      -

      copyright © 2012–2020 by Roderick W. Smith

      +

      copyright © 2012–2021 by Roderick W. Smith

      This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

      Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/docs/refind/hi-theme.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/docs/refind/hi-theme.png differ diff -Nru refind-0.12.0/docs/refind/index.html refind-0.13.2/docs/refind/index.html --- refind-0.12.0/docs/refind/index.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/index.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

      Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

      +3/13/2021, referencing rEFInd 0.13.2

      This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

      @@ -122,7 +122,9 @@

      Introduction

      -

      This page describes rEFInd, my fork of the rEFIt boot manager for computers based on the Extensible Firmware Interface (EFI) and Unified EFI (UEFI). Like rEFIt, rEFInd is a boot manager, meaning that it presents a menu of options to the user when the computer first starts up, as shown below. rEFInd is not a boot loader, which is a program that loads an OS kernel and hands off control to it. (Since version 3.3.0, the Linux kernel has included a built-in boot loader, though, so this distinction is rather artificial these days, at least for Linux.) Many popular boot managers, such as the Grand Unified Bootloader (GRUB), are also boot loaders, which can blur the distinction in many users' minds. All EFI-capable OSes include boot loaders, so this limitation isn't a problem. If you're using Linux, you should be aware that several EFI boot loaders are available, so choosing between them can be a challenge. In fact, the Linux kernel can function as an EFI boot loader for itself, which gives rEFInd characteristics similar to a boot loader for Linux. See my Web page on this topic for more information.

      + + +

      This page describes rEFInd, my fork of the rEFIt boot manager for computers based on the Extensible Firmware Interface (EFI) and Unified EFI (UEFI). Like rEFIt, rEFInd is a boot manager, meaning that it presents a menu of options to the user when the computer first starts up, as shown below. rEFInd is not a boot loader, which is a program that loads an OS kernel and hands off control to it. Many popular boot managers, such as the Grand Unified Bootloader (GRUB), are also boot loaders, which can blur the distinction in many users' minds. All EFI-capable OSes include boot loaders, so this limitation isn't a problem. If you're using Linux, you should be aware that several EFI boot loaders are available, so choosing between them can be a challenge. In fact, since version 3.3.0, the Linux kernel can function as an EFI boot loader for itself, which gives rEFInd characteristics similar to a boot loader for Linux. See my Web page on this topic for more information.


      rEFInd presents a graphical menu for selecting your
@@ -132,13 +134,13 @@
 
 <p>In theory, EFI implementations should provide boot managers. Unfortunately, in practice these boot managers are often so poor as to be useless. The worst I've personally encountered is on <a href=Gigabyte's Hybrid EFI, which provides you with no boot options whatsoever, beyond choosing the boot device (hard disk vs. optical disc, for instance). I've heard of others that are just as bad. For this reason, a good EFI boot manager—either standalone or as part of a boot loader—is a practical necessity for multi-booting on an EFI computer. That's where rEFInd comes into play.

      -

      I decided to fork the earlier rEFIt project because, although rEFIt is a useful program, it suffers from several important limitations, such as poor control over the boot loader detection process and an ability to display at most a handful of boot loader entries on its main screen. Christoph Pfisterer, rEFIt's author, stopped updating rEFIt with version 0.14, which was released in March of 2010. Since I forked rEFIt to rEFInd, Christoph has begun pointing rEFIt users to rEFInd as a successor project.

      +

      rEFInd is a fork of the earlier rEFIt boot manager, which has not seen any development since 2010. Since rEFInd's release, Chrisoph Pfisterer, rEFIt's author, has begun pointing to rEFInd as its successor project. The Clover boot manager is a different fork of rEFIt that's used mainly as a Hackintosh (macOS on stock PC hardware) boot utility. In 2020, RefindPlus emerged as a fork of rEFInd with features intended to support older Macs with third-party video cards, which can be difficult to get working. Thus, the rEFIt family of boot managers has become fairly populous.

      -

      As already noted, rEFInd is a boot manager for EFI and UEFI computers. (I use "EFI" to refer to either version unless the distinction is important.) You're likely to benefit from it on computers that boot multiple OSes, such as two or more of Linux, macOS, and Windows. You will not find rEFInd useful on older BIOS-based computers or on systems with other types of firmware, such as older PowerPC-based Macs. Prior to mid-2011, few computers outside of Intel-based Macs used EFI; but starting in 2011, computer manufacturers began adopting UEFI in droves, so most computers bought since then use EFI. Even so, many modern PCs support both EFI-style booting and BIOS-style booting, the latter via a BIOS compatibility mode that's known as the Compatibility Support Module (CSM). Thus, you may be using BIOS-style booting on an EFI-based computer. My page on the CSM describes how it works and why it can create problems in more detail. If you're unsure which boot method your computer uses, check the first of the subsections, What's Your Boot Mode.

      +

      As already noted, rEFInd is a boot manager for EFI and UEFI computers. (I use "EFI" to refer to either version unless the distinction is important.) You're likely to benefit from rEFInd on computers that boot multiple OSes, such as two or more of Linux, macOS, and Windows. You will not find rEFInd useful on older BIOS-based computers or on systems with other types of firmware, such as older PowerPC-based Macs. Prior to mid-2011, few computers outside of Intel-based Macs used EFI; but starting in 2011, computer manufacturers began adopting UEFI in droves, so most computers bought since then use EFI. Even so, many modern PCs support both EFI-style booting and BIOS-style booting, the latter via a BIOS compatibility mode that's known as the Compatibility Support Module (CSM). Thus, you may be using BIOS-style booting on an EFI-based computer. My page on the CSM describes how it works and why it can create problems in more detail. If you're unsure which boot method your computer uses, check the first of the subsections, What's Your Boot Mode.

      -

      Subsequent sections of this document are on separate pages. Be aware that you probably don't need to read them all; just skip to the sections that interest you:

      + -

      Note: I consider rEFInd to be beta-quality software! That said, rEFInd is a usable program in its current form on many systems. If you have problems, feel free to drop me a line.

      +

      Subsequent sections of this document are on separate pages. Be aware that you probably don't need to read them all; just skip to the sections that interest you.

      @@ -160,7 +162,7 @@
    10. rEFInd and OS X 10.10 (Yosemite)—Apple's OS X 10.10 makes some changes that require your attention (this subpage is rendered obsolete by rEFInd 0.8.4 and later)
    11. -
    12. rEFInd and System Integrity Protection—How to install rEFInd on Macs running OS X 10.11 (El Capitan) +
    13. rEFInd and System Integrity Protection—How to install rEFInd on Macs running OS X 10.11 (El Capitan) and later
    14. Using rEFInd—Basic usage instructions for the boot loader
    15. @@ -199,7 +201,7 @@ -

      References and Additional Information

      +

      References and Additional Information

        @@ -208,9 +210,7 @@
          -
        • The EFI Boot Process describes, in broad strokes, how EFI systems boot.
        • - -
        • The EFI System Partition and the Default Boot Behavior covers the EFI boot process in more technical terms and in greater detail, as well as how Fedora's fallback.efi program works.
        • +
        • The EFI System Partition and the Default Boot Behavior is a document that covers the EFI boot process, including how Fedora's fallback.efi program works.
        • A Linux kernel mailing list thread describing the new EFI stub loader that was introduced in the Linux 3.3 kernel series.
        • @@ -220,15 +220,17 @@
        • My Linux on UEFI: A Quick Installation Guide page provides helpful tips on how to install Linux on EFI-based systems.
        • -
        • Phoenix Technologies maintains a wiki on EFI topics, including information on many EFI system calls useful to programmers.
        • -
        • Matthew J. Garrett, the developer of the shim boot loader to manage Secure Boot, maintains a blog in which he often writes about EFI issues.
        • Adam Williamson has written a good summary of what EFI is and how it works.
        • -
        • J. A. Watson has a review of rEFInd on an HP laptop on ZDNet. He had serious problems because of the HP's UEFI bugs, but finally got it to work.
        • +
        • J. A. Watson has a review of rEFInd on an HP laptop on ZDNet in 2013. He had serious problems because of the HP's UEFI bugs, but finally got it to work.
        • + +
        • James Jesudason has a tutorial (also from 2013) on installing Ubuntu 13.04 beta on a Macbook Retina Pro on this blog page. I'd recommend using a Linux filesystem driver to read the kernel directly from a Linux filesystem rather than copy the kernel to the OS X partition as in the tutorial, but either method will work.
        • + +
        • This 2017 page describes how to set up a multi-boot of five Linux distributions and Windows using rEFInd. The method described was sub-optimal in a few ways (such as re-installing rEFInd in each distribution rather than using refind-mkdefault to adjust the boot order), but it does work.
        • -
        • James Jesudason has a tutorial on installing Ubuntu 13.04 beta on a Macbook Retina Pro on this blog page. I'd recommend using a Linux filesystem driver to read the kernel directly from a Linux filesystem rather than copy the kernel to the OS X partition as in the tutorial, but either method will work.
        • +
        • A more recent (2020) review and tutorial by Tony George describes setting up a computer with rEFInd to triple-boot three Linux distributions.
        • The Windows MBR2GPT utility, part of Windows 10 Creator's Update, can convert a Windows computer that boots in BIOS mode from an MBR disk to one that boots in EFI mode from a GPT disk. Note that I've never used this tool, and I have no idea how it would cope with a multi-boot configuration.
        • @@ -236,7 +238,7 @@
        • If you're interested in developing EFI software yourself, my Programming for EFI can help you get started.
        • -
        • This page describes how to set up a multi-boot of five Linux distributions and Windows using rEFInd. The method described was sub-optimal in a few ways (such as re-installing rEFInd in each distribution rather than using refind-mkdefault to adjust the boot order), but it does work.
        • +
        • Phoenix Technologies maintained a wiki on EFI topics, including information on many EFI system calls useful to programmers. Unfortunately, these pages seem to have disappeared. Fortunately, they are archived by the Wayback Machine, here and here.
        @@ -274,7 +276,7 @@
        -

        copyright © 2012–2020 by Roderick W. Smith

        +

        copyright © 2012–2021 by Roderick W. Smith

        This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

        diff -Nru refind-0.12.0/docs/refind/installing.html refind-0.13.2/docs/refind/installing.html --- refind-0.12.0/docs/refind/installing.html 2020-03-13 12:48:33.000000000 +0000 +++ refind-0.13.2/docs/refind/installing.html 2021-03-14 00:04:03.000000000 +0000 @@ -4,20 +4,20 @@ - The rEFInd Boot Manager: Installing rEFInd + The rEFInd Boot Manager: Installing and Uninstalling rEFInd -

        The rEFInd Boot Manager:
        Installing rEFInd

        +

        The rEFInd Boot Manager:
        Installing and Uninstalling rEFInd

        by Roderick W. Smith, rodsmith@rodsbooks.com

        Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

        +3/13/2021, referencing rEFInd 0.13.2

        This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

        @@ -132,7 +132,9 @@ - + + + @@ -211,23 +213,23 @@ -

        Installing rEFInd Using an RPM or Debian Package File

        +

        Installing rEFInd Using an RPM or Debian Package File

        I provide RPM and Debian package files for rEFInd; and I maintain an Ubuntu PPA for rEFInd. If you have a working RPM-based or Debian-based Linux installation that boots in EFI mode, using one of these files is likely to be the easiest way to install rEFInd: You need only download the file and issue an appropriate installation command. In some cases, double-clicking the package in your file manager will install it. If that doesn't work, a command like the following will install the RPM on an RPM-based system:

        -
        # rpm -Uvh refind-0.12.0-1.x86_64.rpm
        +
        # rpm -Uvh refind-0.13.2-1.x86_64.rpm

        On a Debian-based system, the equivalent command is:

        -
        # dpkg -i refind_0.12.0-1_amd64.deb
        +
        # dpkg -i refind_0.13.2-1_amd64.deb
        - +

        Either command produces output similar to that described for using the refind-install script, so you can check it for error messages and other signs of trouble. The package file installs rEFInd and registers it with the EFI to be the default boot loader. The script that runs as part of the installation process tries to determine if you're using Secure Boot, and if so it will try to configure rEFInd to launch using shim; however, this won't work correctly on all systems. Since version 0.11.0, refind-install supports storing Secure Boot private keys in an encrypted form. If you set up rEFInd in this way, the RPM or Debian package will fail to install, since it assumes an unencrypted Secure Boot key.

        -

        If you're using Ubuntu, you should be able to install using the PPA as follows:

        +

        If you're using Ubuntu, you should be able to install using the PPA as follows:

        $ sudo apt-add-repository ppa:rodsmith/refind
         $ sudo apt-get update
        @@ -235,7 +237,7 @@
         
         
         
        -

        The PPA version asks if you want to install rEFInd to your ESP. (Chances are you want to respond affirmatively.) The PPA version will update automatically with your other software, which you might or might not want to have happen. It's also built with GNU-EFI rather than with TianoCore. This last detail should have no practical effects, but it might be important if you've got a buggy EFI or if there's some undiscovered rEFInd bug that interacts with the build environment.

        +

        The PPA version asks if you want to install rEFInd to your ESP. (Chances are you want to respond affirmatively.) The PPA version will update automatically with your other software, which you might or might not want to have happen. It's also built with GNU-EFI rather than with TianoCore. This last detail should have no practical effects.

        The installation script makes an attempt to install rEFInd in a bootable way even if you run the script from a BIOS-mode boot, and therefore the RPM and Debian packages do the same. I cannot guarantee that this will work, though, and even if it does, some of the tricks that refind-install uses might not persist for long. You might therefore want to use mvrefind to move your rEFInd installation to another name after you boot Linux for the first time from rEFInd.

        @@ -244,11 +246,25 @@

        Installing rEFInd Using refind-install under Linux or MacOS

        -

        If you're using Linux or macOS, and if you can't or don't want to use a distribution package file, the easiest way to install rEFInd is to use the refind-install script. This script automatically copies rEFInd's files to your ESP or other target location and makes changes to your firmware's NVRAM settings so that rEFInd will start the next time you boot. If you've booted to macOS or in non-Secure-Boot EFI mode to Linux on a UEFI-based PC, refind-install will probably do the right thing, so you can get by with the quick instructions. If your setup is unusual, if your computer uses Secure Boot, or if you want to create a USB flash drive with rEFInd on it, you should read the man page for this utility.

        + + +

        If you're using Linux or macOS, and if you can't or don't want to use a distribution package file, the easiest way to install rEFInd is to use the refind-install script. This script automatically copies rEFInd's files to your ESP or other target location and makes changes to your firmware's NVRAM settings so that rEFInd will start the next time you boot. If you've booted to macOS or in non-Secure-Boot EFI mode to Linux on a UEFI-based PC, refind-install will probably do the right thing, so you can get by with the quick instructions on this page. If your setup is unusual, if your computer uses Secure Boot, or if you want to create a USB flash drive with rEFInd on it, you should read the man page for this utility. A few highlights of changes you might want to include are:

        + +
          + +
        • The --shim shimfile option points to a Shim binary, for use with a Secure Boot installation. If you're currently booting with Secure Boot active, you can point to a working Shim file with this option, as in refind-install --shim /boot/efi/EFI/fedora/shimx64.efi to use Fedora's shimx64.efi file. The refind-install script will then make a copy of this file in rEFInd's own installation directory and make other necessary changes. This option, and the next two, are valid only in Linux. Note that this option is not necessary if you've taken complete control of Secure Boot on your computer, as described here.
        • + +
        • The --localkeys option tells rEFInd to re-sign (or sign) its binaries with keys that refind-install generates itself, or that it generated on a previous run of the program. (These keys are stored in /etc/refind.d/keys.) This option is necessary when using Secure Boot with unsigned binaries, such as if you built the program yourself from source code. If you're using my binary .zip file, then the binaries are signed with my own key, and this option is unnecessary—but if you prefer to use your own keys, you can use this option.
        • + +
        • The --encryptkeys option causes rEFInd's local keys stored in /etc/refind.d/keys to be encrypted. If you use local keys, use of this option is highly desirable.
        • + +
        • The --ownhfs device-file option is a macOS-specific option that causes rEFInd to install itself to an HFS+ volume that you specify by device filename, as in ./refind-install --ownhfs /dev/disk2s4. In principle, this type of installation permits selecting between a direct macOS boot and a boot through rEFInd using Apple's own boot manager or macOS's Startup Disk tool. In practice, though, this feature has not been well maintained and so I can't promise it will work. Also, installing to an HFS+ volume will require commenting out the use_nvram false line in refind.conf if you want to hide boot tags or rely on rEFInd's ability to remember its last-booted OS.
        • + +
        -

        By default, the refind-install script installs rEFInd to your disk's ESP. Under macOS, you can instead install rEFInd to your current macOS boot partition by passing the script the --notesp option, or to a non-boot HFS+ partition by using the --ownhfs devicefile option. Under either OS, you can install to something other than the currently-running OS by using the --root /mountpoint option.

        +

        By default, the refind-install script installs rEFInd to your disk's ESP. Under macOS, you can instead install rEFInd to your current macOS boot partition by passing the script the --notesp option. Under either OS, you can install to something other than the currently-running OS by using the --root /mountpoint option.

        Under Linux, refind-install will be most reliable if your ESP is already mounted at /boot, /boot/efi, or /efi, as described in more detail in the Installing rEFInd Manually Using Linux section. (If you installed Linux in EFI mode, chances are your ESP is properly mounted.) If your ESP is not so mounted, refind-install will attempt to locate and mount an ESP, but this action is not guaranteed to work correctly. If you run refind-install from a BIOS/legacy-mode boot, particularly on a computer that also runs Windows, you should be aware that the tricks the script uses to install itself from BIOS mode are rather delicate. You can convert to a more conventional configuration using the mvrefind script after you've booted in EFI mode.

        @@ -288,6 +304,8 @@ Unmounting install dir
        + +

        In either case, the details of the output differ depending on your existing configuration and how you ran the program. Unless you see an obvious warning or error, you shouldn't be concerned about minor deviations from these examples. If you run into such a situation, or if you want to install in an unusual way, read on....

        Note that the change to an ESP location for rEFInd with version 0.8.4 means that, if you upgrade rEFInd from an earlier version, you may notice a rEFInd boot option in the rEFInd menu. This option will boot the old version of rEFInd (or the new one, if something went wrong and the old version continues to boot). You can rid yourself of the unwanted boot menu by deleting the old files or by using dont_scan_dirs or dont_scan_files in refind.conf. Before you do this, you should use rEFInd to identify the unwanted files—the filename and volume identifier appear under the icons when you highlight the option. You can then locate and delete them from within macOS. Before you delete the old files, though, you may want to copy over any changes you've made to the rEFInd configuration, icons, and other support files.

        @@ -350,17 +368,17 @@ -

        Before installing rEFInd on a Mac, you must determine whether it uses a 32-bit or 64-bit EFI implementation. Most Intel-based Macs have 64-bit EFIs, so you should use the refind_x64.efi file with them; but very early Intel-based Macs have 32-bit EFIs (and sometimes 32-bit CPUs), which require the refind_ia32.efi file. You can determine whether your Mac needs the x86-64 or IA32 build by typing the following command in a Mac Terminal window:

        +

        Before installing rEFInd on a Mac, you must determine the computer's architecture. Most Intel-based Macs have 64-bit EFIs, so you should use the refind_x64.efi file with them; but very early Intel-based Macs have 32-bit EFIs (and sometimes 32-bit CPUs), which require the refind_ia32.efi file. Beginning in late 2020, Apple has begun moving to ARM CPUs, so you should theoretically use the refind_aa64.efi binary; however, I do not know if this binary will work on the new ARM-based Macs! If you have such a computer, I strongly recommend testing with rEFInd on a USB flash drive before you attempt to install it to your computer. You can determine which binary your Mac needs by typing the following command in a Mac Terminal window:

         $ ioreg -l -p IODeviceTree | grep firmware-abi
         
        -

        The result should include either EFI32 or EFI64, indicating that you should use the refind_ia32.efi or refind_x64.efi binary, respectively.

        +

        The result should include either EFI32 or EFI64, indicating that you should use the refind_ia32.efi or refind_x64.efi binary, respectively. (I don't know what this command returns on ARM-based Macs.)

        -

        You should also be aware of your macOS version and installation options. If you used whole-disk encryption (WDE) or a logical volume for installation, you cannot install to the macOS root partition; you must install to the ESP or to a separate HFS+ partition. WDE became an option with macOS 10.7 and logical volumes are the default in macOS 10.10 and later. If in doubt, proceed with an installation to the ESP or to a separate HFS+ partition.

        +

        You should also be aware of your macOS version and installation options. If you used whole-disk encryption (WDE) or a logical volume for installation, you cannot install to the macOS root partition; you must install to the ESP or to a separate HFS+ partition. WDE became an option with macOS 10.7 and logical volumes are the default in macOS 10.10 and later. If in doubt, proceed with an installation to the ESP or to a separate HFS+ partition. (Using a separate HFS+ partition had advantages in the past, but today I recommend installing to the ESP rather than to a dedicated HFS+ partition.)

        - +

        The procedure for installing rEFInd on a Mac is similar to that for installing it under Linux, except that you must use the bless utility rather than efibootmgr to register the program with the firmware. Also, you'll probably have to mount your ESP manually, since that's not done by default under macOS. To be precise, you should follow these steps:

        @@ -394,17 +412,16 @@ changing into the rEFInd package's main directory.
      • Remove the files for the versions of rEFInd you're not using, as in sudo rm Volumes/esp/efi/refind/refind_ia32.efi - Volumes/esp/efi/refind/refind_aa64.efi on a Mac with a 64-bit EFI + class="userinput">sudo rm /Volumes/esp/efi/refind/refind_ia32.efi + /Volumes/esp/efi/refind/refind_aa64.efi on a Mac with a 64-bit EFI or sudo rm /Volumes/ESP/efi/refind/refind_x64.efi - Volumes/esp/efi/refind/refind_aa64.efi on a Mac with a 32-bit + /Volumes/esp/efi/refind/refind_aa64.efi on a Mac with a 32-bit EFI.
      • Optionally, remove the drivers directories for the architectures you're - not using—/Volumes/ESP/efi/refind/drivers_ia32 or - /Volumes/ESP/efi/refind/drivers_x64, as appropriate. (No Mac - uses an ARM CPU, so you'd also remove - /Volumes/ESP/efi/refind/drivers_aa64.
      • + not using—/Volumes/ESP/efi/refind/drivers_aa64, + /Volumes/ESP/efi/refind/drivers_ia32, or + /Volumes/ESP/efi/refind/drivers_x64, as appropriate.
      • I strongly recommend that you remove some or all of the drivers for the architecture you are using; if you don't need them, they'll slow down @@ -447,6 +464,8 @@
    + +

    When you reboot, your Mac should bring up the rEFInd menu, and should continue to do so thereafter. If you make changes that break this association, you can re-run the bless command (if necessary, restoring the rEFInd files first). This might be necessary after installing system updates from Apple or if you upgrade rEFInd to a newer version.

    If you're replacing rEFIt, you may discover that rEFInd works on the first boot, but the system reverts back to rEFIt or a direct boot to macOS on the second boot. To fix this problem, you can remove the rEFItBlesser program, which is located at /Library/StartupItems/rEFItBlesser. This program attempts to keep rEFIt set as the default boot loader, but it also has the purpose of protecting the computer from launching the wrong OS after waking from sleep. If you want that protection, my suggestion is to install rEFIt and rEFItBlesser and then replace the refit.efi file with refind_x64.efi or refind_ia32.efi (renaming it to refit.efi). Used in this way, rEFInd will still look for its own configuration file, refind.conf, so you'll need to move it but not rename it. If you don't move the icons from the rEFInd package, your icons will continue to look like rEFIt icons, and you'll be missing the new icons for specific Linux distributions that rEFInd provides. One final caveat: It's conceivable that rEFItBlesser is what's causing filesystem corruption for some users, so if you've been having this problem with rEFIt, it might be worth disabling this program and not using it with rEFInd.

    @@ -457,7 +476,9 @@ -

    I know relatively little about Windows EFI management tools; however, I do know that at least two relevant tools exist: the standard bcdedit and the third-party EasyUEFI. Because Windows is not my primary OS, I'm less familiar with its tools and procedures than I am with those used by Linux. Some third-party documentation, such as this blog post, may be of help if you run into problems installing rEFInd in Windows.

    +

    I know relatively little about Windows EFI management tools; however, I do know that at least three relevant tools exist: the standard bcdedit, the open source text-mode efibootwin, and the third-party GUI EasyUEFI. Because Windows is not my primary OS, I'm less familiar with its tools and procedures than I am with those used by Linux. Some third-party documentation, such as this blog post, may be of help if you run into problems installing rEFInd in Windows.

    + +

    The efibootwin tool is similar to the standard Linux efibootmgr tool. As far as I can tell, it is available only as source code files with Microsoft Visual C++ project build files. As I don't use that development environment, I haven't tried building binaries myself and I don't know how well the program, once built, would work. Thus, I do not rely on it in the following procedure, although it might be quite useful. I may look into it in more depth in the future.

    The EasyUEFI tool is GUI tool for managing EFI boot programs. At one time it was free, but the latest version costs $29.95, albeit with a free trial available. It's fairly intuitive and easy to use, but I don't have detailed instructions on how to use it. If you want to use EasyUEFI, you'll have to use it in place of bcdedit at the end of the following procedure.

    @@ -465,15 +486,15 @@
      -
    1. Locate Command Prompt in the Start menu, right-click it, and select Run as Administrator. This action opens a Command Prompt window with administrative privileges.
    2. +
    3. Locate Command Prompt in the Start menu, right-click it, and select Run as Administrator. This action opens a Command Prompt window with administrative privileges. (Be sure to use the stock Command Prompt program. Some alternatives, such as PowerShell, are known not to work with this procedure, or at least to require changes that I don't document here.)
    4. -
    5. Type mountvol S: /S in the Administrator Command Prompt window. This makes the ESP accessible as drive S: from that window. (You can use a drive identifier other than S: if you like.)
    6. +
    7. Type mountvol R: /S in the Administrator Command Prompt window. This makes the ESP accessible as drive R: from that window. (You can use a drive identifier other than R: if you like.)
    8. Change into the main rEFInd package directory, so that the refind subdirectory is visible when you type dir.
    9. -
    10. Type xcopy /E refind S:\EFI\refind\ to copy the refind directory tree to the ESP's EFI directory. If you omit the trailing backslash from this command, xcopy will ask if you want to create the refind directory. Tell it to do so.
    11. +
    12. Type xcopy /E refind R:\EFI\refind\ to copy the refind directory tree to the ESP's EFI directory. If you omit the trailing backslash from this command, xcopy will ask if you want to create the refind directory. Tell it to do so.
    13. -
    14. Type S: to change to the ESP.
    15. +
    16. Type R: to change to the ESP.
    17. Type cd EFI\refind to change into the refind subdirectory
    18. @@ -511,7 +532,7 @@ -
    19. rEFInd will install Linux filesystem drivers for filesystems that it has detected, plus the HFS+ driver if it detects HFS+ on non-Apple hardware. rEFInd will not install non-Linux drivers or drivers for filesystem it has not detected. The practical upshot of this is that if you install rEFInd in this way before installing Linux, rEFInd may not be able to load your Linux kernel directly. It's better to install Linux before rEFInd.
    20. +
    21. rEFInd will install Linux filesystem drivers for filesystems that it has detected, plus the HFS+ driver if it detects HFS+ on non-Apple hardware. rEFInd will not install non-Linux drivers or drivers for filesystems it has not detected. The practical upshot of this is that if you install rEFInd in this way before installing Linux, rEFInd may not be able to load your Linux kernel directly. It's better to install Linux before rEFInd.
    22. The feature creates a new NVRAM entry for the new installation or might detect and use an existing entry instead. An entry for a previous installation using another method is unlikely to be used, so you might end up with duplicates; but repeated installations from rEFInd are unlikely to produce duplicate entries.
    23. +
    24. In some of my tests, this installation method causes a ~30-second delay to the boot process on at least some Macs. The cause seems to be that the boot entry is similar to the one created by bless when the --shortform option is not used, as described later, in Fixing Macintosh Boot Problems. I've never seen this problem on UEFI-based PCs, though, and I don't know how common it is among Macs.
    25. +

      The intent of this installation method is that you can boot rEFInd from a CD-R or USB flash drive and use that to install rEFInd to your hard disk or to recover from a damaged installation or from a boot coup.

      @@ -661,7 +684,7 @@

      Alternative Naming Options

      -

      Some EFI implementations do a poor job of honoring the boot options set via Linux's efibootmgr or other tools. You may also lack access to such utilities, such as if you must install rEFInd in Windows. In such cases, you may need to change the boot loader's name so that the EFI will see it as the default boot loader. rEFInd should then boot when your NVRAM lacks information on specific boot loaders to use. Broadly speaking, there are two alternative names that are most useful:

      +

      Some EFI implementations (mostly from before 2014) do a poor job of honoring the boot options set via Linux's efibootmgr or other tools. You may also lack access to such utilities, such as if you must install rEFInd in Windows. In such cases, you may need to change the boot loader's name so that the EFI will see it as the default boot loader. rEFInd should then boot when your NVRAM lacks information on specific boot loaders to use. Broadly speaking, there are two alternative names that are most useful:

      @@ -819,7 +842,7 @@
      • shell.efi—This + href="https://github.com/tianocore/edk2/releases/download/edk2-stable201911/ShellBinPkg.zip">shell.efi—This file, placed in the ESP's EFI/tools directory, adds the ability to launch a text-mode EFI shell from rEFInd. Note that the download link is to a zip file that contains binaries for multiple architectures. Additional shell download links appear on the Using EFI Drivers page for more on this topic.
      • @@ -898,6 +921,8 @@ detail on the Managing Secure Boot page. + +
      • iPXE—This tool provides the ability to boot a computer from a network server. Consult the BUILDING.txt file in the rEFInd source code package for @@ -920,7 +945,7 @@

        Fixing Macintosh Boot Problems

        -

        I've received a few reports of a sluggish boot process (a delay of about 30 seconds before starting rEFInd) on some Macs after installing rEFInd, as well as some other Mac-specific peculiarities. I've been unable to replicate thess problems myself, and their true causes remains mysterious to me. I have found several possible solutions, though: Using the --shortform option, using the fallback filename, moving rEFInd to an HFS+ volume, clearing NVRAM entries, fixing wake problems, and fixing a failure to find Linux. Fortunately, these problems are much less common in early 2020 than they were a few years ago.

        +

        I've received a few reports of a sluggish boot process (a delay of about 30 seconds before starting rEFInd) on some Macs after installing rEFInd, as well as some other Mac-specific peculiarities. I've been unable to replicate thess problems myself, and their true causes remains mysterious to me. I have found several possible solutions, though: Using the --shortform option, using the fallback filename, moving rEFInd to an HFS+ volume, clearing NVRAM entries, fixing wake problems, and fixing a failure to find Linux. Fortunately, these problems are much less common in early 2021 than they were a few years ago.

        Using the --shortform Option

        @@ -928,7 +953,7 @@

        Prior to version 0.8.5, these instructions and the refind-install script omitted the --shortform option from the bless command when installing rEFInd to the ESP. A rEFInd user, however, discovered that using the option eliminated the 30-second delay, so it is now the default with 0.8.5's refind-install, and is specified in the instructions. If you installed rEFInd 0.8.4 or earlier, you may want to re-install or re-bless rEFInd using this option.

        -

        There is one caveat, though: The man page for bless notes that --shortform notes that its use can come "at the expense of boot time performance." Thus, it's not clear to me that this option might not actually create problems on some computers. (It's eliminated the boot delay on my 2014 MacBook Air and has no detrimental effect on an old 32-bit Mac Mini that's never had a boot delay problem, though.) Thus, if you have problems with rEFInd 0.8.5 or later, you might try running bless, as described in Installing rEFInd Manually Using macOS's step 9, but omit the --shortform option.

        +

        There is one caveat, though: The man page for bless notes that --shortform its use can come "at the expense of boot time performance." Thus, it's not clear to me that this option might not actually create problems on some computers. (It's eliminated the boot delay on my 2014 MacBook Air and has no detrimental effect on an old 32-bit Mac Mini that's never had a boot delay problem, though.) Thus, if you have problems with rEFInd 0.8.5 or later, you might try running bless, as described in Installing rEFInd Manually Using macOS's step 9, but omit the --shortform option.

        Using the Fallback Filename

        @@ -940,13 +965,11 @@

        Moving rEFInd to an HFS+ Volume

        -

        Most of the reports of sluggish Macintosh boots I've seen note that the user installed rEFInd to the ESP rather than to the macOS root partition. Some users have reported that re-installing rEFInd to the macOS root partition clears up the problem. This is obviously a straightforward solution to the problem, if it works. (This location is not an option when using WDE or macOS logical volumes.) Note that rEFInd can launch boot loaders that are stored on any partition that the EFI can read no matter where it's installed; therefore, you'll still be able to launch boot loaders stored on the ESP (or elsewhere) if you install it in this way.

        - -

        A variant of this solution is to create a small (~100MiB) HFS+ volume to be used exclusively by rEFInd. You can then install rEFInd to that volume with the --ownhfs option to refind-install, as in ./refind-install --ownhfs /dev/disk0s6 if the volume is /dev/disk0s6. This approach has the advantage that it can be managed via macOS's own Startup Disk tool in System Preferences.

        +

        In the mid-2010s, many reports of sluggish boot problems on Macs were tied to installations to the ESP. One solution discovered at the time was to install rEFInd on an HFS+ volume — either the system root (/) partition or a small (~100 MiB) HFS+ partition dedicated to rEFInd. This solution worked at the time, but modern macOS installations generally use APFS in a logical volume, making a dedicated HFS+ partition the only way to do this today.

        -

        The biggest drawback to storing rEFInd on an HFS+ volume is that you won't be able to edit the rEFInd configuration file or move rEFInd-related binaries from an EFI shell if you install it in this way, since Apple's HFS+ driver for EFI is read-only. (The same is true of rEFInd's HFS+ driver, so it won't help you overcome this limitation.) You may also be limited in making changes to your rEFInd configuration from Linux or other OSes, too, since Linux's HFS+ drivers disable write support by default on volumes with an active journal. You can force write access in Linux by using the force option to mount; however, this procedure is noted as being risky in the Linux HFS+ documentation, so I don't recommend doing this on a regular basis on the macOS boot volume. This isn't as risky if you use a dedicated HFS+ rEFInd partition, though. You could even mount it as the Linux /boot partition, in which case it would also hold the Linux kernel and related files.

        +

        A more important reason to not use this approach is that it's no longer necessary. The use of --shortform as an option to bless, as noted in the previous subsection, seems to have largely eliminated the problem. Installing to a dedicated HFS+ partition has several drawbacks, too, including an inability to write to the partition from the EFI itself. This will affect rEFInd's ability to store variables outside of NVRAM and limit your ability to edit rEFInd's configuration file from an EFI shell, should the need arise. You may also be limited in making changes to your rEFInd configuration from Linux or other OSes, too, since Linux's HFS+ drivers disable write support by default on volumes with an active journal. You can force write access in Linux by using the force option to mount; however, this procedure is noted as being risky in the Linux HFS+ documentation, so I don't recommend doing this on a regular basis on the macOS boot volume. This isn't as risky if you use a dedicated HFS+ rEFInd partition, though. You could even mount it as the Linux /boot partition, in which case it would also hold the Linux kernel and related files.

        -

        A variant of this solution is suggested in this blog post, which recommends placing rEFInd on an HFS+ volume on the first SATA channel. (In the blogger's case, that channel used to hold an optical drive, but that drive was replaced by a hard disk.)

        +

        In early 2021, refind-install still supports the --ownhfs option, but the relevant code has not been maintained, and I can't promise it will work well. Overall, therefore, I don't recommend this approach; however, if you're having problems with boot delays introduced by rEFInd, and if other solutions don't work, it might be worth a try.

        Clearing the NVRAM Entries

        @@ -988,7 +1011,7 @@
          -
        • A malfunctioning BIOS/legacy boot—If you installed Linux in BIOS/legacy mode, as most online documentation suggests, it could be that your hybrid MBR is missing or damaged. The usual symptom of this problem is that rEFInd shows a generic Linux penguin icon and that selecting it produces a message to the effect that a bootable OS could not be found. As hybrid MBRs are ugly and dangerous, I recommend avoiding them if possible, so my preferred solution to this problem is to set up EFI filesystem drivers and boot that way; however, fixing the hybrid MBR may be an easier solution. This is especially true if you installed a 32-bit version of Linux on a 64-bit Mac (or a 64-bit version on a rare Mac with a 64-bit CPU but a 32-bit EFI).
        • +
        • A malfunctioning BIOS/CSMlegacy boot—If you installed Linux in BIOS mode, as most online documentation suggested up until at least the mid-2010s, it could be that your hybrid MBR is missing or damaged. The usual symptom of this problem is that rEFInd shows a generic Linux penguin icon and that selecting it produces a message to the effect that a bootable OS could not be found. As hybrid MBRs are ugly and dangerous, I recommend avoiding them if possible, so my preferred solution to this problem is to set up EFI filesystem drivers and boot that way; however, fixing the hybrid MBR may be an easier solution. This is especially true if you installed a 32-bit version of Linux on a 64-bit Mac (or a 64-bit version on a rare Mac with a 64-bit CPU but a 32-bit EFI).
        • EFI filesystem driver problems—Ideally, rEFInd should be able to load and run your Linux kernel directly, but this approach normally requires you to have a working EFI driver for the filesystem that holds your Linux kernel. This won't always be the case; and even if it is installed, there can be interference from other drivers, so you may need to remove the drivers that you don't use. If drivers are the root of your problem, you won't see any Linux options, or you'll see the one penguin icon (as above) with no others that point to your Linux kernel(s).
        • @@ -1020,11 +1043,11 @@
        • Type exit to exit from diskutil.
        • -
        • 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.
        • +
        • Type cd /d R:\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.
        • Type bootrec /fixboot.
        • -
        • 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.
        • +
        • Type bcdboot c:\Windows /s R: /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.
        • 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.
        • @@ -1129,7 +1152,7 @@

          Uninstalling rEFInd from Windows

          -

          From Windows, you must reverse the directions for installing in Windows—type mountvol S: /S to mount your ESP as S:, then navigate to the S:\EFI directory and delete the refind subdirectory.

          +

          From Windows, you must reverse the directions for installing in Windows—type mountvol R: /S to mount your ESP as R:, then navigate to the R:\EFI directory and delete the refind subdirectory.

          Post-Uninstallation Activity (UEFI-Based PCs)

          @@ -1157,7 +1180,7 @@
          -

          copyright © 2012–2020 by Roderick W. Smith

          +

          copyright © 2012–2021 by Roderick W. Smith

          This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

          diff -Nru refind-0.12.0/docs/refind/linux.html refind-0.13.2/docs/refind/linux.html --- refind-0.12.0/docs/refind/linux.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/linux.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

          Originally written: 3/19/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

          +3/13/2021, referencing rEFInd 0.13.2

          This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

          @@ -155,7 +155,7 @@

          Under Linux, by contrast, things can get complicated. As detailed on my Managing EFI Boot Loaders for Linux page, several different EFI boot loaders for Linux exist, and all of them require configuration. If you're lucky, your distribution will have set up a Linux boot loader in a sensible way, in which case rEFInd should detect it and it will work as easily as a Windows or macOS boot loader. If you're not lucky, though, you may need to configure it further. rEFInd offers options to help out with this task. Naturally, rEFInd supports traditional Linux boot loaders. It works even better with the Linux EFI stub loader, so I provide instructions on starting with it. For those interested in manual configuration, I also provide detailed instructions on how the EFI stub support works and how to configure it.

          -

          Using a Traditional Linux Boot Loader

          +

          Using a Traditional Linux Boot Loader

          I consider ELILO, GRUB Legacy, GRUB 2, and SYSLINUX to be traditional Linux boot loaders. These programs all exist independent of the Linux kernel, but they can load a kernel and hand off control to it. All four programs have their own configuration files that reside in the same directory as the boot loader itself (or optionally elsewhere, in the case of GRUB 2).

          @@ -176,7 +176,7 @@

          This method requires that your /boot directory, whether it's on a separate partition or is a regular directory in your root (/) filesystem, be readable by the EFI. At the moment, all EFI implementations can read FAT and Macs can read HFS+. By using drivers, you can make any EFI read HFS+, ISO-9660, ReiserFS, ext2fs, ext3fs, ext4fs, Btrfs, or other filesystems. Thus, if you use any of these filesystems on a regular partition (not an LVM or RAID configuration) that holds your kernels in /boot, you qualify for this easy method. The default partition layouts used by Ubuntu, Fedora, and many other distributions qualify, because they use one of these filesystems (usually ext4fs) in a normal partition or on a separate /boot partition. You must also have a 3.3.0 or later Linux kernel with EFI stub support, of course.

          -

          If you installed rEFInd 0.6.0 or later with its refind-install (formerly install.sh) script from your regular Linux installation, chances are everything's set up; you should be able to reboot and see your Linux kernels as boot options. If you installed manually, from macOS, or from an emergency system, though, you may need to do a couple of things manually: +

          If you installed rEFInd with its refind-install script from your regular Linux installation, chances are everything's set up; you should be able to reboot and see your Linux kernels as boot options. If you installed manually, from macOS, or from an emergency system, though, you may need to do a couple of things manually:

            @@ -187,7 +187,8 @@
          • Create a refind_linux.conf file in your /boot directory. The mkrlconf script that comes with rEFInd should do - this job, or you can do it manually as described later. rEFInd can create minimal boot options from /etc/fstab, if /boot is not a separate partition, so a refind_linux.conf file may not be strictly @@ -239,7 +240,7 @@
    -

    You can continue to boot your computer with this type of configuration; however, the drawback is that you'll need to copy your kernel whenever it's updated. This can be a hassle. A better way is to configure you system so that the EFI, and therefore rEFInd, can read your Linux /boot directory, where most Linux distributions place their kernels. You do this by installing the appropriate EFI filesystem driver for the /boot (or root, /) filesystem.

    +

    You can continue to boot your computer with this type of configuration; however, the drawback is that you'll need to copy your kernel whenever it's updated. This can be a hassle. A better way is to configure your system so that the EFI, and therefore rEFInd, can read your Linux /boot directory, where most Linux distributions place their kernels. You do this by installing the appropriate EFI filesystem driver for the /boot (or root, /) filesystem.

    If You Need to Reconfigure Your Partitions....

    @@ -292,13 +293,6 @@ already exist, the easiest way to create it is to run the mkrlconf script. -
  • Check your refind.conf file (presumably in - /boot/EFI/refind) to be sure that the - scan_all_linux_kernels line is either commented out or set to - true, on, or 1. If it's set to - false, off, or 0, uncomment the line or - change it to true.
  • -
  • Optionally, type cp /boot/EFI/refind/icons/os_name.png /boot/.VolumeIcon.png to give loaders in /boot an icon for your distribution.
  • @@ -313,7 +307,7 @@

    For these reasons, a variant of this procedure is desirable on some systems. Most of the steps are similar, but in this variant, you create a separate /boot partition that's independent of the ESP. This partition can use FAT, HFS+, ReiserFS, ext2fs, ext3fs, ext4fs, or Btrfs; but if you use any of the last six filesystems (five on Macs), you must install the matching EFI filesystem driver that ships with rEFInd. (You can use other filesystems if you obtain EFI filesystem drivers from another source, such as Pete Batards' efifs project.) Creating the filesystem will normally require you to shrink an existing partition by a suitable amount (500–1024MiB). Be sure to shrink the partition from its end point, since this is both safer and faster than shrinking the partition from its start. The GParted program, which comes with most distributions, can shrink partitions; but you may need to run it from an emergency disk if you want to shrink your root (/) partition or any other partition that you can't unmount. Mount your new /boot partition at a temporary location, copy or move the current /boot files into it, unmount it, and add it to /etc/fstab as /boot.

    -

    If your distribution already uses a separate /boot partition, but if it uses XFS or some other unsuitable filesystem, I recommend you first try a driver from Pete Batards' efifs project. If you don't want to do this or if the driver doesn't work, you can back up /boot, create a fresh FAT, HFS+, ReiserFS, Btrfs, ext2, ext3, or ext4 filesystem on it, and restore the original files. You'll probably need to adjust the UUID value in /etc/fstab to ensure that the computer mounts the new filesystem when you boot. If you use a separate non-ESP /boot partition, you'll probably want to continue mounting the ESP at /boot/efi.

    +

    If your distribution already uses a separate /boot partition, but if it uses XFS or some other unsuitable filesystem, I recommend you first try a driver from Pete Batards' efifs project. If you don't want to do this or if the driver doesn't work, you can back up /boot, create a fresh FAT, HFS+, ReiserFS, Btrfs, ext2, ext3, or ext4 filesystem on it, and restore the original files. You'll probably need to adjust the UUID value and filesystem type in /etc/fstab to ensure that the computer mounts the new filesystem when you boot. If you use a separate non-ESP /boot partition, you'll probably want to continue mounting the ESP at /boot/efi.

    EFI Stub Loader Support Technical Details

    @@ -341,7 +335,7 @@
      -
    1. rEFInd looks for boot loaders whose names include the strings +
    2. rEFInd looks for boot loaders whose names begin with the strings bzImage, kernel, or vmlinuz. For instance, bzImage-4.15.0.efi or vmlinuz-5.4.0 would match, and trigger subsequent steps in this procedure. The @@ -367,7 +361,8 @@
    3. rEFInd looks for an initial RAM disk in the same directory as the kernel file. A matching initial RAM disk has a name that begins with - init and that includes the same version string as the kernel. + init or booster and that includes the same version + string as the kernel. The version string is defined as the part of the filename from the first digit to the last digit, inclusive. Note that the version string can include non-digits. For instance, the version string for @@ -530,7 +525,7 @@ -

      Ordinarily, a kernel booted in this way must reside on the ESP, or at least on another FAT partition. On a Macintosh, though, you can use HFS+ to house your kernel files. In fact, that may be necessary; my Mac Mini hangs when I try to boot a Linux kernel via an EFI stub loader from the computer's ESP, but it works fine when booting from an HFS+ partition. If you use EFI drivers, though, you can place your kernel on any filesystem for which an EFI driver exists. This list is currently good (ext2fs/ext3fs, ext4fs, ReiserFS, Btrfs, ISO-9660, and HFS+ in rEFInd, plus more from other sources), so chances are you'll be able to use this method to boot your kernel from your root (/) partition or from a /boot partition.

      +

      Ordinarily, a kernel booted in this way must reside on the ESP, or at least on another FAT partition. On a Macintosh, though, you can use HFS+ to house your kernel files. In fact, that may be necessary; my Mac Mini hangs when I try to boot a Linux kernel via an EFI stub loader from the computer's ESP, but it works fine when booting from an HFS+ partition. If you use EFI drivers, though, you can place your kernel on any filesystem for which an EFI driver exists. Currently, rEFInd provides drivers for ext2fs/ext3fs, ext4fs, ReiserFS, Btrfs, ISO-9660, and HFS+; plus more drivers are available from other sources. Thus, chances are you'll be able to use this method to boot your kernel from your root (/) partition or from a /boot partition.

      rEFInd sorts boot loader entries within each directory by time stamp, so that the most recent entry comes first. Thus, if you specify a directory name (or a volume label, for loaders stored in a volume's root directory) as the default_selection, rEFInd will make the most recent loader in the directory the default. This can obviate the need to adjust this configuration parameter when you add a new kernel; chances are you want the most recently-added kernel to be the default, and rEFInd makes it so when you set the default_selection in this way. If you don't want the latest kernel to become the default, you can use touch to give the desired kernel (or other boot loader) in the directory a more recent time stamp, or you can set default_selection to a value that uniquely identifies your desired default loader. One caveat you should keep in mind is that the EFI and Windows interpret the hardware clock as local time, whereas macOS uses Coordinated Universal Time (UTC). Linux can work either way. Thus, time stamps for boot loaders can be skewed by several hours depending on the environment in which they were created or last modified.

      @@ -542,7 +537,7 @@
      -

      copyright © 2012–2020 by Roderick W. Smith

      +

      copyright © 2012–2021 by Roderick W. Smith

      This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

      diff -Nru refind-0.12.0/docs/refind/mkrlconf.html refind-0.13.2/docs/refind/mkrlconf.html --- refind-0.12.0/docs/refind/mkrlconf.html 2020-03-13 13:22:49.000000000 +0000 +++ refind-0.13.2/docs/refind/mkrlconf.html 2021-03-14 00:36:14.000000000 +0000 @@ -4,7 +4,7 @@ Man page of MKRLCONF

      MKRLCONF

      -Section: rEFInd Manual (8)
      Updated: 0.12.0
      Index +Section: rEFInd Manual (8)
      Updated: 0.13.2
      Index Return to Main Contents
        @@ -92,6 +92,6 @@ This document was created by man2html, using the manual pages.
      -Time: 13:22:49 GMT, March 13, 2020 +Time: 00:36:14 GMT, March 14, 2021 Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/docs/refind/MokManager1.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/docs/refind/MokManager1.png differ diff -Nru refind-0.12.0/docs/refind/mvrefind.html refind-0.13.2/docs/refind/mvrefind.html --- refind-0.12.0/docs/refind/mvrefind.html 2020-03-13 13:22:49.000000000 +0000 +++ refind-0.13.2/docs/refind/mvrefind.html 2021-03-14 00:36:14.000000000 +0000 @@ -4,7 +4,7 @@ Man page of MVREFIND

      MVREFIND

      -Section: rEFInd Manual (8)
      Updated: 0.12.0
      Index +Section: rEFInd Manual (8)
      Updated: 0.13.2
      Index Return to Main Contents
        @@ -147,6 +147,6 @@ This document was created by man2html, using the manual pages.
      -Time: 13:22:49 GMT, March 13, 2020 +Time: 00:36:14 GMT, March 14, 2021 Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/docs/refind/papirus-theme.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/docs/refind/papirus-theme.png differ diff -Nru refind-0.12.0/docs/refind/refind-install.html refind-0.13.2/docs/refind/refind-install.html --- refind-0.12.0/docs/refind/refind-install.html 2020-03-13 13:22:49.000000000 +0000 +++ refind-0.13.2/docs/refind/refind-install.html 2021-03-14 00:36:14.000000000 +0000 @@ -4,7 +4,7 @@ Man page of REFIND-INSTALL

      REFIND-INSTALL

      -Section: rEFInd Manual (8)
      Updated: 0.12.0
      Index +Section: rEFInd Manual (8)
      Updated: 0.13.2
      Index Return to Main Contents
        @@ -349,6 +349,6 @@ This document was created by man2html, using the manual pages.
      -Time: 13:22:49 GMT, March 13, 2020 +Time: 00:36:14 GMT, March 14, 2021 diff -Nru refind-0.12.0/docs/refind/refind-mkdefault.html refind-0.13.2/docs/refind/refind-mkdefault.html --- refind-0.12.0/docs/refind/refind-mkdefault.html 2020-03-13 13:22:49.000000000 +0000 +++ refind-0.13.2/docs/refind/refind-mkdefault.html 2021-03-14 00:36:14.000000000 +0000 @@ -4,7 +4,7 @@ Man page of REFIND-MKDEFAULT

      REFIND-MKDEFAULT

      -Section: rEFInd Manual (8)
      Updated: 0.12.0
      Index +Section: rEFInd Manual (8)
      Updated: 0.13.2
      Index Return to Main Contents
        @@ -205,6 +205,6 @@ This document was created by man2html, using the manual pages.
      -Time: 13:22:49 GMT, March 13, 2020 +Time: 00:36:14 GMT, March 14, 2021 diff -Nru refind-0.12.0/docs/refind/revisions.html refind-0.13.2/docs/refind/revisions.html --- refind-0.12.0/docs/refind/revisions.html 2020-03-13 13:19:56.000000000 +0000 +++ refind-0.13.2/docs/refind/revisions.html 2021-03-14 00:25:00.000000000 +0000 @@ -16,7 +16,7 @@

      by Roderick W. Smith, rodsmith@rodsbooks.com

      -

      Last Web page update: 2/12/2020

      +

      Last Web page update: 3/13/2021

      This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

      @@ -126,6 +126,155 @@
        +
      • 0.13.2 (3/13/2021)—I've fixed a number of bugs and added a few new features with this version: + +
          + +
        • This version fixes a memory management bugs that could cause rEFInd + to hang under certain unpredictable circumstances.
        • + +
        • I've also fixed potential memory management bugs that could cause + HFS+, ext2fs, and ext4fs drivers to misbehave.
        • + +
        • Version 0.13.1 introduced a bug that caused rEFInd to fail to load + properly signed EFI drivers when Secure Boot was enabled and used + via Shim. This is now fixed.
        • + +
        • The mvrefind script has long had an unnoticed bug that + caused it to fail to create a new NVRAM entry to point to the moved + rEFInd.
        • + +
        • MacOS 11 ("Big Sur") changed something in its sed utility + that caused the mountesp script to fail in that version of + macOS. This problem has been fixed.
        • + +
        • I've improved Secure Boot support in the refind-install + script when run from Linux: The script can now install a Secure Boot + key using mokutil, which can slightly simplify the MOK + setup. It also now creates a backup NVRAM entry to boot directly, + rather than via a Shim, if installed with the --shim + option. This should help on systems with custom Secure Boot key sets + that don't rely on Shim, but with a stray Shim binary present on the + ESP that a package script will detect and pass as a --shim + option to rEFInd. If this configuration causes a Secure Boot failure + on launching Shim, then it should fail over to the direct boot and + be fine. (Some EFIs will hang rather than fail over to the next + entry, though, and this change won't help with them.)
        • + +
        • This version implements more logging improvements, including bumping + the maximum log_level from 3 to 4.
        • + +
        • This version improves the separation of filesystem vs. partition + names internally. This change can make for more accurate OS icon + choices in some situations, such as if both names are set but only + the partition name contains useful information.
        • + +
        • Previously, when setting use_nvram false, rEFInd would use + the vars subdirectory of its own directory; and if that was + unwritable (say, if it was on an HFS+ volume), then rEFInd would not + store its own variables, such as PreviousBoot and + HiddenTags. With this version, rEFInd will now use the + vars subdirectory of rEFInd's own directory (as before); + but if that's not available, it will use a directory called + refind-vars in the root directory of the first ESP that + rEFInd can locate; and if neither is available, rEFInd will use + NVRAM storage instead, as if use_nvram true had been + set.
        • + +
        • I've made rEFInd's self-install code less finicky about the success + of copying configuration, icons, and driver files. Previously it + reported a failure and did not modify NVRAM if any of these failed + to copy. Now it should register itself, although it does complain on + screen and log details (if logging is enabled). This can help if + installing from a source that lacks drivers for a detectable Linux + filesystem (ext2/3/4fs, ReiserFS, Btrfs, or HFS+).
        • + +
        • I've adjusted reporting of SIP values by adding back 0x77 as an + interpreted value for on-screen notification when rotating through + values.
        • + +
        • rEFInd can now identify JFS volumes as such, if a third-party JFS + driver is installed.
        • + +
        • This version adds support for Booster initrd files, which are named + booster*, rather than init*. See https://github.com/anatol/booster + and/or https://wiki.archlinux.org/index.php/Booster + for more on Booster.
        • + +
        • I've added an icon for Manjaro Linux.
        • + +
        • I've swapped out expired CentOS Secure Boot keys for new ones.
        • + +
        • GNU-EFI 3.0.13 made changes to its header files that caused rEFInd + build failures. These are now fixed.
        • + +
      • + +
      + +
        +
      • 0.13.1 (2/24/2021)—This release adds a couple of new features that won't impact most people (although one is a fairly major addition) and several small bug fixes and minor changes: + +
          + +
        • The biggest new feature is a logging facility: If log_level is set to a value higher than 0, rEFInd will log its actions in a UTF-16 file called refind.log in the same directory as the rEFInd binary, or on the first accessible ESP if rEFInd was launched from a read-only filesystem (such as an HFS+ volume). This feature is intended to assist in debugging problems, not for regular use, since it can slow rEFInd's operation. Thus, it should be commented out or set to its default value of 0 in most cases. If you need to use it, values from 1 to 3 are currently supported, with higher values doing more logging. I'm likely to expand the amount of logging done, and may increase beyond level 3, in the future.
        • + +
        • To support the systemd Boot Loader Interface, rEFInd can now write the GUID of the partition from which rEFInd was launched to an EFI variable called LoaderDevicePartUUID whenever rEFInd launches a Linux kernel with EFI stub support, ELILO, or GRUB. (rEFInd does not touch this variable when launching macOS or Windows.) When this variable is present, systemd can use it to mount the ESP at /boot or /efi, if one of these directories is present and empty. This activity is enabled by setting write_systemd_vars true in refind.conf.
        • + +
        • rEFInd now checks EFI variables before writing them, and will not re-write them if doing so would not change them. Previously, rEFInd did this for its PreviousBoot variable; but now it does so for all writes to EFI variables, so as to minimize wear and tear on NVRAM.
        • + +
        • I have uncommented use_nvram and set it to false in refind.conf. This will cause new installations to save rEFInd's own variables to disk rather than to NVRAM, to save wear and tear on the latter; however, if rEFInd is installed to a read-only filesystem (most likely HFS+), then features such as boot to the most-recently-booted OS and dynamic tag hiding will not work unless use_nvram is commented out or set to true. This change will not affect upgrades to existing installations.
        • + +
        • I have reverted some of the video device discovery changes made in version 0.13.0 after receiving a bug report indicating that they caused at least one Mac model to hang.
        • + +
        • Fixed a bug that could cause rEFInd to misbehave or hang if a disabled manual boot stanza referenced a volume with a name that existed but was unreadable.
        • + +
        • Version 0.13.0 failed to record boots to EFI-based boot options; thus, they would not be used as the default boot option when default_selection + was used on the next boot. This has now been fixed.
        • + +
        • Updated LodePNG library (which rEFInd uses for PNG support) to version 20201017.
        • + +
        • Additional replacements of which with command -v in mvrefind.
        • + +
      • + +
      • 0.13.0 (2/15/2021)—This release implements one major user-visible change, several bug fixes (including one major one), and a few smaller improvements: + +
          + +
        • The big new feature is implementation of a reboot into a firmware-specified boot option—that is, one that's stored in the computer's NVRAM and presented by the EFI's own boot manager. This feature enables network booting, booting into exotic firmware-provided tools, and booting in ways that might bypass some problems. It can enable full-resolution HiDPI displays in macOS, for instance, which has long been a limitation of rEFInd. This feature also enables launching an EFI shell provided by the firmware, not just ones that are installed as EFI binaries on the hard disk. See this section of the documentation for a detailed description of this new ability.
        • + +
        • I've fixed a memory management bug that I believe was responsible for rEFInd 0.12.0 hanging on a few (mostly Apple) computers.
        • + +
        • I've imported several minor features and one mostly-behind-the-scenes change to video mode setting from RefindPlus: + +
            + +
          • The list of Apple SIP/CSR values has been expanded to match changes in the values that Apple uses.
          • + +
          • The ext4fs driver now supports the EXT4_FEATURE_INCOMPAT_ENCRYPT flag, which means that filesystems that provide directory-level encryption are now supported, although rEFInd can read only from unencrypted directories on such filesystems.
          • + +
          • Aspects of RefindPlus's video mode detection have been added to rEFInd. In testing, this change doesn't make much difference, but it's a base from which future improvements can be made.
          • + +
          • A new option to the resolution token in refind.conf, max, tells rEFInd to set the maximum possible video resolution. This works well on most computers, but there's a chance that it will set a resolution that's higher than your monitor supports, so it's not the default, as it is in RefindPlus.
          • + +
        • + +
        • Improved rEFInd's detection of duplicated ext2/3/4 filesystems (as on some types of software RAID); the old code incorrectly marked filesystems with different UUIDs but the same label as duplicates.
        • + +
        • Fixed a bug that caused the mouse pointer to disappear on 64-bit systems when it was moved.
        • + +
        • Fixed a bug that caused EFI 1.x (UGA graphics) systems to hang if refind.conf has a resolution line that specified only one value. (This is a valid configuration for EFI 2.x/GOP graphics systems, but not for EFI 1.x/UGA.) When such a configuration is detected now, it is ignored.
        • + +
        • Replaced which with command -v in shell scripts. This should be more robust in theory.
        • + +
        • Fixed compiler problems with GCC versions 9 and 10.
        • + +
      + +
        +
      • 0.12.0 (3/13/2020)—This release sees a couple of major new features and some other more minor changes: @@ -1016,7 +1165,7 @@
        -

        copyright © 2012–2020 by Roderick W. Smith

        +

        copyright © 2012–2021 by Roderick W. Smith

        This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

        diff -Nru refind-0.12.0/docs/refind/secureboot.html refind-0.13.2/docs/refind/secureboot.html --- refind-0.12.0/docs/refind/secureboot.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/secureboot.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

        Originally written: 11/13/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

        +3/13/2021, referencing rEFInd 0.13.2

        This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

        @@ -148,12 +148,18 @@

        If you're using a computer that supports Secure Boot, you may run into extra complications. This feature is intended to make it difficult for malware to insert itself early into the computer's boot process. Unfortunately, it also complicates multi-boot configurations such as those that rEFInd is intended to manage. This page describes some Secure Boot basics and two specific ways of using rEFInd with Secure Boot: Using the Shim program and using the PreLoader program. (My separate EFI Boot Loaders for Linux page on Secure Boot covers the additional topics of disabling Secure Boot and adding keys to the firmware's own set of keys.) This page concludes with a look at known bugs and limitations in rEFInd's Secure Boot features.

        -

        Note that Macs don't (yet?) support Secure Boot, but -as of version 10.11 ("El Capitan"), macOS uses its own new security feature, -System Integrity Protection (SIP), which creates its own set of -hoops through which rEFInd users must jump. See the rEFInd and System Integrity Protection page for -details.

        +

        As of version 10.11 ("El Capitan"), macOS uses its own new security +feature, System Integrity Protection (SIP), which creates its own set +of hoops through which rEFInd users must jump. See the rEFInd and System Integrity Protection page for details. +Macs that include the T2 +security chip (introduced in 2018) support a feature that Apple calls +Secure Boot, but it's unclear to me if this is the same as UEFI Secure Boot. +See Apple's Secure Boot +documentation for details. If it is the same, then this page should +apply to the latest Macs, although you'll need to adjust Secure Boot through +Apple's Startup Security Utility rather than through a PC-style firmware +setup utility outside of the OS.

        Basic Issues

        @@ -163,9 +169,9 @@ If you don't want it, you can
        disable it, at least on x86-64 PCs. If an ARM-based computer ships with -Windows 8, this isn't an option for it. Unfortunately, the Shim and PreLoader programs described on this page currently support only x86-64, not x86 or ARM.

        +Windows 8 or later, this isn't an option for it. Unfortunately, the PreLoader program described on this page currently supports only x86-64, not x86 or ARM; however, Shim is available for both x86-64 and ARM. I've never used an ARM system with Secure Boot enabled, though.

        -

        Microsoft requires that non-server computers that display Windows 8 logos ship with Secure Boot enabled. As a practical matter, this also means that such computers ship with Microsoft's keys in their firmware. In the absence of an industry-standard body to manage the signing of Secure Boot keys, this means that Microsoft's key is the only one that's more-or-less guaranteed to be installed on the computer, thus blocking the ability to boot any OS that lacks a boot path through Microsoft's signing key. In other words, although it's not specified this way in the UEFI specification, Microsoft is effectively the Secure Boot gatekeeper.

        +

        Microsoft requires that non-server computers that display Windows 8 or later logos ship with Secure Boot enabled. As a practical matter, this also means that such computers ship with Microsoft's keys in their firmware. In the absence of an industry-standard body to manage the signing of Secure Boot keys, this means that Microsoft's key is the only one that's more-or-less guaranteed to be installed on the computer, thus blocking the ability to boot any OS that lacks a boot path through Microsoft's signing key. In other words, although it's not specified this way in the UEFI specification, Microsoft is effectively the Secure Boot gatekeeper.

        Fortunately, Microsoft will sign third-party binaries with their key—or more precisely, with a key that Microsoft uses to sign third-party binaries. (Microsoft uses another key to sign its own binaries, and some devices, such as the Microsoft Surface tablet, lack the third-party Microsoft key.) A payment of $99 to Verisign enables a software distributor to sign as many binaries as desired. Red Hat (Fedora), Novell (SUSE), Canonical (Ubuntu), and several smaller distributions are all using this system to enable their boot loaders to run. ALT Linux provides a how-to document on having a binary signed with Microsoft's key, if you're interested in the details. Unfortunately, using a third-party signing service is an awkward solution for open source software. In fact, for this very reason two separate programs exist that shift the Secure Boot "train" from Microsoft's proprietary "track" to one that's more friendly to open source authors. Both of these programs (Shim and PreLoader) are available in binary form signed by Microsoft's key. PreLoader enables the computer to launch binaries that the user has explicitly identified as being OK. Shim enables the computer to launch binaries that are signed by a key that's built into it or that the user adds to a list known as the Machine Owner Key (MOK) list. Recent versions of Shim also support single-binary registrations, much as PreLoader does. Distributions beginning with Ubuntu 12.10 (and 12.04.2), Fedora 18, and OpenSUSE 12.3 use Shim, although Ubuntu 12.10 initially shipped with an early version of Shim that's useless for launching rEFInd because it lacked support for the MOK list. (Current versions of Ubuntu ship with more flexible versions of Shim.) PreLoader is used by some smaller and more specialized distributions, such as Arch Linux. You can switch from one to the other if you like, no matter what your distribution uses by default. Shim is definitely the more popular of these programs, and is more likely to work correctly in most situations, although there are exceptions to this rule.

        @@ -181,7 +187,8 @@ Microsoft's keys with your own, in order to take full control of Secure Boot on your computer. The trouble is that this process is tedious and varies in details from one computer to another. It's worth - noting that a few computers ship with Canonical's key, which can help + noting that in the past, a few computers shipped with Canonical's key, + which can help slightly when booting Ubuntu; if your computer is so equipped, you can use any Shim you like and not worry about adding Canonical's key to your MOK list, although you must still add a MOK entry for rEFInd @@ -213,9 +220,11 @@
      -

      All three key types are the same in form—Shim's built-in keys and MOKs are both generated using the same tools used to generate Secure Boot keys. The keys can be generated with the common openssl program, but signing EFI binaries requires either of two rarer programs: sbsign or pesign. If you use Shim with a distribution that doesn't support Secure Boot, you'll need to either sign the kernels yourself, which can be a hassle, or launch the kernels by way of a boot loader that doesn't check for signatures, such as ELILO.

      +

      All three key types are the same in form—Shim's built-in keys and MOKs are both generated using the same tools used to generate Secure Boot keys. The keys can be generated with the common openssl program, but signing EFI binaries requires either of two rarer programs: sbsign or pesign. If you use Shim with a distribution that doesn't support Secure Boot, you'll need to either sign the kernels yourself, which can be a hassle, or launch the kernels by way of a boot loader that doesn't check for signatures, such as ELILO. (Note, however, that many Linux distributions have begun to enforce a chain of trust beyond the boot loader and kernel. This means that if you try to boot using ELILO or some other tool that doesn't enforce Secure Boot, subsequent parts of the boot process may fail.)

      -

      PreLoader and recent versions of Shim are easier to set up on a distribution that doesn't support Secure Boot because these tools don't require the use of keys; instead, you can tell them which binaries you trust and they will let you launch them. This works well on a system with boot managers, boot loaders, and kernels that seldom change. It's not a good solution for distribution maintainers, though, because it requires that users manually add binaries to the MOK's list of approved binaries when the OS is installed and every time those binaries change. Also, PreLoader relies on a helper program, HashTool, to enroll hashes. (This is Geek for "tell the computer that a binary is OK.") Unfortunately, the initial (and, as far as I know, only signed) HashTool can enroll hashes only from the partition from which it was launched, so if you want to use rEFInd to launch Linux kernels directly, it's easiest if you mount your EFI System Partition (ESP) at /boot in Linux or copy your kernels to the ESP. Another approach is to copy HashTool.efi to the partition that holds your kernel and rename it to almost anything else. rEFInd will then treat it like an OS boot loader and create a menu entry for it, enabling you to launch it as needed. Recent versions of Shim's key- and hash-management tool, MokManager, supports reading keys and binaries from any partition that the EFI can read.

      +

      Both Secure Boot and Shim support a sort of anti-authorization key or hash. These keys or hashes identify binaries that must not be launched — typically, they're known malware, or at least they're known to contain bugs that could be exploited to create security problems.

      + +

      PreLoader and recent versions of Shim are easier to set up on a distribution that doesn't support Secure Boot because these tools don't require the use of keys; instead, you can tell them which binaries you trust and they will let you launch them. This works well on a system with boot managers, boot loaders, and kernels that seldom change. It's not a good solution for distribution maintainers, though, because it requires that users manually add binaries to the MOK's list of approved binaries when the OS is installed and every time those binaries change. Also, PreLoader relies on a helper program, HashTool, to enroll hashes. ("Hash" is Geek for "tell the computer that a binary is OK.") Unfortunately, the initial (and, as far as I know, only signed) HashTool can enroll hashes only from the partition from which it was launched, so if you want to use rEFInd to launch Linux kernels directly, it's easiest if you mount your EFI System Partition (ESP) at /boot in Linux or copy your kernels to the ESP. Another approach is to copy HashTool.efi to the partition that holds your kernel and rename it to almost anything else. rEFInd will then treat it like an OS boot loader and create a menu entry for it, enabling you to launch it as needed. Recent versions of Shim's key- and hash-management tool, MokManager, support reading keys and binaries from any partition that the EFI can read.

      rEFInd can communicate with the Shim system to authenticate boot loaders. If a boot loader has been signed by a valid UEFI Secure Boot key, a valid Shim key, or a valid MOK, rEFInd will launch it. rEFInd will also launch unsigned boot loaders or those with invalid signatures if Secure Boot is disabled in or unsupported by the firmware. (If that's your situation, you needn't bother reading this page.) PreLoader is designed in such a way that it requires no explicit support in rEFInd to work.

      @@ -225,7 +234,7 @@

      Using rEFInd with Shim

      -

      Because several major distributions support Shim, I describe it first. You may need to adjust the rEFInd installation process to get it working with Shim, especially if you're not using a distribution that uses this software. In addition to installing Shim, you should know how to manage your MOKs, so I describe this topic, too. If you don't want to use Shim, you can skip ahead to the section on PreLoader. Note also that you can use Shim with hashes to identify individual binaries rather than with keys. This usage of Shim is much more like the PreLoader procedure, although a few details differ.

      +

      Because several major distributions support Shim, I describe it first. You may need to adjust the rEFInd installation process to get it working with Shim, especially if you're not using a distribution that uses this software. In addition to installing Shim, you should know how to manage your MOKs, so I describe this topic, too. If you don't want to use Shim, you can skip ahead to the section on PreLoader; however, in 2021 PreLoader offers few advantages and many disadvantages compared to Shim, so I strongly recommend using Shim rather than PreLoader. Note also that you can use Shim with hashes to identify individual binaries rather than with keys. This usage of Shim is much more like the PreLoader procedure, although a few details differ.

      Installing Shim and rEFInd

      @@ -237,13 +246,13 @@
        -
      • Shim—You can use any version of Shim you like, except for version 0.1, which doesn't support MOKs. In many cases, Shim will already be installed on your computer from your distribution, called shim.efi or shimx64.efi in the distribution's directory on the ESP. If so, it's probably best to use that version, since its built-in key will handle your distribution's kernels. If you don't currently have a Shim installed, you can copy one from another computer, copy the file from a distribution installation disc, or download a version of Shim 0.2 (old, but still usable) signed with Microsoft's Secure Boot key here. This version (created by Shim's developer, former Red Hat employee Matthew J. Garrett) includes a Shim key that's used by nothing but the MokManager.efi program that also ships with the program. No matter what version of Shim you use, you must enroll rEFInd's MOK. You should install Shim just as you would install other EFI boot loaders, as described here. For use in launching rEFInd, it makes sense to install shim.efi in EFI/refind on your ESP, although of course this detail is up to you.
      • +
      • Shim—You can use any version of Shim you like, except for version 0.1, which doesn't support MOKs. In many cases, Shim will already be installed on your computer from your distribution, called shim.efi or shimx64.efi in the distribution's directory on the ESP. If so, it's probably best to use that version, since its built-in key will handle your distribution's kernels. If you don't currently have a Shim installed, you can copy one from another computer or copy the file from a distribution installation disc. No matter what version of Shim you use, you must enroll rEFInd's MOK. You should install Shim just as you would install other EFI boot loaders, as described here. For use in launching rEFInd, you should install shimx64.efi in EFI/refind (or wherever rEFInd is installed) on your ESP.
      • -
      • MokManager—This program is included with Shim 0.2 and later. It presents a user interface for managing MOKs, and it's launched by Shim if Shim can't find its default boot loader (generally grubx64.efi) or if that program isn't properly signed. In principle, this program could be signed with a Secure Boot key or a MOK, but such binaries are usually signed by Shim keys. This program should reside in the same directory as shim.efi, under the name MokManager.efi, mmx64.efi, or another architecture-specific variant of that. Although you could theoretically do without MokManager, in practice you'll need it at least temporarily to install the MOK with which rEFInd is signed.
      • +
      • MokManager—This program is included with Shim 0.2 and later. It presents a user interface for managing MOKs, and it's launched by Shim if Shim can't find its default boot loader (generally grubx64.efi) or if that program isn't properly signed. In principle, this program could be signed with a Secure Boot key or a MOK, but such binaries are usually signed by Shim keys. This program should reside in the same directory as shimx64.efi, under the name MokManager.efi, mmx64.efi, or another architecture-specific variant of that. Although you could theoretically do without MokManager, in practice you'll need it at least temporarily to install the MOK with which rEFInd is signed.
      • -
      • rEFInd—Naturally, you need rEFInd. Because Shim is hard-coded to launch a program called grubx64.efi, you must install rEFInd using that name and to the same directory in which shim.efi resides. In theory, rEFInd could be signed with a Secure Boot key, a Shim key, or a MOK; however, because Microsoft won't sign binaries distributed under the GPLv3, I can't distribute a version of rEFInd signed with Microsoft's Secure Boot key; and as I don't have access to the private Shim keys used by any distribution, I can't distribute a rEFInd binary signed by them. (If distributions begin including rEFInd in their package sets, though, such distribution-provided binaries could be signed with the distributions' Shim keys. This appears to be the case with the rEFInd binaries distributed with ALT Linux, according to its package description.) Thus, rEFInd will normally be signed by a MOK. Beginning with version 0.5.0, rEFInd binaries that I provide are signed by me. Beginning with version 0.5.1, the installation script provides an option to sign the rEFInd binary with your own key, provided the necessary support software is installed.
      • +
      • rEFInd—Naturally, you need rEFInd. Because Shim is hard-coded to launch a program called grubx64.efi, you must install rEFInd using that name and to the same directory in which shimx64.efi resides. In theory, rEFInd could be signed with a Secure Boot key, a Shim key, or a MOK; however, because Microsoft won't sign binaries distributed under the GPLv3, I can't distribute a version of rEFInd signed with Microsoft's Secure Boot key; and as I don't have access to the private Shim keys used by any distribution, I can't distribute a rEFInd binary signed by them. (Distributions can provide rEFInd binaries signed with the their own Shim keys. This appears to be the case with the rEFInd binaries distributed with ALT Linux, according to its package description. On the other hand, Ubuntu, for one, signs their GRUB binaries but not their rEFInd binaries.) Thus, rEFInd will normally be signed by a MOK. Beginning with version 0.5.0, rEFInd binaries that I provide are signed by me. Beginning with version 0.5.1, the installation script provides an option to sign the rEFInd binary with your own key, provided the necessary support software is installed.
      • -
      • Your boot loaders and kernels—Your OS boot loaders, and perhaps your Linux kernels, must be signed. They can be signed with any of the three key types. Indeed, your system may have a mix of all three types—a Windows 8 or later boot loader will most likely be signed with Microsoft's Secure Boot key, GRUB and kernels provided by most distributions will be signed with their own Shim keys, and if you use your own locally-compiled kernel or a boot loader from an unusual source you may need to sign it with a MOK. Aside from signing, these files can be installed in exactly the same way as if your computer were not using Secure Boot.
      • +
      • Your boot loaders and kernels—Your OS boot loaders, and usually your Linux kernels, must be signed. They can be signed with any of the three key types. Indeed, your system may have a mix of all three types—a Windows 8 or later boot loader will most likely be signed with Microsoft's Secure Boot key, GRUB and kernels provided by most distributions will be signed with their own Shim keys, and if you use your own locally-compiled kernel or a boot loader from an unusual source you may need to sign it with a MOK. Aside from signing, these files can be installed in exactly the same way as if your computer were not using Secure Boot.
      @@ -259,28 +268,28 @@ zip or CD-R image file). If you download the binary zip file, unzip it; if you get the CD-R image file, burn it to a CD-R and mount it.
    4. -
    5. Download Shim from Matthew J. Garrett's - download site or from your distribution. (Don't use an early 0.1 +
    6. Download Shim from your distribution. (Don't use an early 0.1 version, though; as noted earlier, it's inadequate for use with rEFInd.)
    7. - + -
    8. Copy the shim.efi and MokManager.efi binaries to the - directory you intend to use for rEFInd—for instance, - EFI/refind on the ESP.
    9. +
    10. Copy the shimx64.efi and MokManager.efi (or more + commonly today, mmx64.efi) binaries to the directory you intend + to use for rEFInd—for instance, EFI/refind on the + ESP.
    11. Follow the installation instructions for rEFInd on the Installing rEFInd page; however, you should normally give rEFInd the filename grubx64.efi and register - shim.efi with the EFI by using efibootmgr in Linux or + shimx64.efi with the EFI by using efibootmgr in Linux or bcdedit in Windows. Be sure that rEFInd (as - grubx64.efi), shim.efi, and MokManager.efi + grubx64.efi), shimx64.efi, and + MokManager.efi/mmx64.efi all reside in the same directory. If you're using Shim 0.7 or later and are installing it under Linux, you may optionally keep rEFInd's refind_x64.efi name; but you must then tell Shim to use rEFInd - by passing an additional -u "shim.efi refind_x64.efi" option to + by passing an additional -u "shimx64.efi refind_x64.efi" option to efibootmgr. (In early 2020, I discovered that some recent Shim binaries include a bug that prevents Shim from launching anything but grubx64.efi and its other internally-coded files. I haven't yet @@ -292,45 +301,81 @@ ideally to a location with few other files. (The rEFInd installation directory should work fine.)
    12. -
    13. Reboot. With any luck, you'll see a simple text-mode user interface - with a label of Shim UEFI key management. This is the - MokManager program, which Shim launched when rEFInd failed verification - because its key is not yet enrolled.
    14. - -
    15. Press your down arrow key and press Enter to select Enroll key from - disk. The screen will clear and prompt you to select a key, as - shown here: +
    16. Optionally, type mokutil -i refind.cer, + adding whatever directory components are needed to access + refind.cer; or you can substitute your own key file if you + re-sign the rEFInd binaries, as described later, in Managing Your MOKs. You will be asked to enter a + password, which is for temporary use only and need not match your user + or root password. This action will store the rEFInd public key + to the NVRAM, enabling MokManager to access it more easily. In theory, + this step obviates the previous one; but it's generally a good idea to + have rEFInd's Secure Boot public key on the boot disk so that it can be + re-enrolled manually, if necessary.
    17. + +
    18. Reboot. With any luck, you'll see a simple text-mode user interface with + a label of Shim UEFI key management. This is the MokManager + program, which Shim launched when rEFInd failed verification because its + key is not yet enrolled. You may be prompted to press a key to begin MOK + management. You have only ten seconds to do so, or the boot will + continue without enrolling the MOK, and rEFInd will probably not launch. + What happens when you begin MOK management depends of whether you used + mokutil to install the MOK....
    19. -
      MokManager's user interface is crude but effective.
      +
    20. If you did not use mokutil, then you must locate and + enroll the rEFInd key file as follows: + +
        + +
      1. Press your down arrow key and press Enter to select Enroll key + from disk; or if you used mokutil earlier, instead + select Enroll MOK. The screen will clear and, if you did + not use mokutil to install the key, prompt you to + select a key, as shown here: + +
        Recent versions of MokManager provide a somewhat
+      more user-friendly user interface.
      2. + + (Early versions of MokManager used a more primitive user interface + with white and yellow text on a black background. If this is what you + see, some details will differ, but the program should still work. You + might want to find more recent programs for the better user interface + and other updated features, though.) + +
      3. Each of the lines with a long awkward string represents a disk + partition. Select one and you'll see a list of files. Continue + selecting subdirectories until you find the refind.cer or + refind_local.cer file you copied to the ESP earlier. (Note + that in the early user interface the long lines can wrap and hide + valid entries on the next line, so you may need to select a disk whose + entry is masked by another one!)
      4. - This user interface was used in early versions of MokManager, but - somewhere between versions 0.4 and 0.7, the user interface received an - upgrade. If you've got a more recent version, it will look more like - this: - -
        Recent versions of MokManager provide a somewhat more
-    user-friendly user interface.
        - -
      5. Each of the lines with a long awkward string represents a disk - partition. Select one and you'll see a list of files. Continue selecting - subdirectories until you find the refind.cer or - refind_local.cer file you copied to the ESP earlier. (Note that - in the early user interface the long lines can wrap and hide valid - entries on the next line, so you may need to select a disk whose entry - is masked by another one!)
      6. - -
      7. Select refind.cer or refind_local.cer. You can type - 1 to view the certificate's details if you - like, or skip that and type 0 to enroll the - key.
      8. +
      9. Select refind.cer or refind_local.cer. You can type + 1 to view the certificate's details if you + like, or skip that and type 0 to enroll the + key.
      10. -
      11. Back out of any directories you entered and return to the MokManager - main menu.
      12. +
      13. Back out of any directories you entered and return to the MokManager + main menu.
      14. -
      15. Select Continue boot at the main menu.
      16. +
    21. + +
    22. If you used mokutil, the task is slightly simplified: + +
        + +
      1. Select Enroll MOK from the main menu.
      2. + +
      3. You may optionally view the key, but to enroll it, you must select + Continue.
      4. + +
      5. MokManager will now ask for verification, and then for a password. + Enter the password you specified when you used mokutil.
      6. + +
    23. + +
    24. Select Reboot at the main menu.
    @@ -376,12 +421,15 @@ and are stored in /etc/refind.d/keys/.
  • Copy the three key files to a secure location and adjust permissions - such that only you can read refind_local.key. You'll need - these keys to sign future binaries, so don't discard them.
  • + such that only you can read refind_local.key. You'll need these + keys to sign future binaries, so don't discard them. Ideally, these keys + should be stored on a USB flash drive kept in a safe; however, just how + far you want to go with security is up to you.
  • Copy the refind_local.cer file to your ESP, ideally to a - location with few other files. (MokManager's user interface becomes - unreliable when browsing directories with lots of files.)
  • + location with few other files. (Some versions of MokManager's user + interface becomes unreliable when browsing directories with lots of + files.)
  • Download and install the sbsigntool package for your distribution. If your distribution doesn't provide this program, you can @@ -530,6 +578,24 @@ href="https://www.rodsbooks.com/efi-bootloaders/secureboot.html#disable">Disabling Secure Boot may be the best solution to such problems.
  • +
  • In 2020, a bug in GRUB 2, known as Boot + Hole, was discovered. This bug could theoretically enable an + attacker to run malicious code in the pre-boot environment. Subsequent + analyses discovered several other potential security bugs in GRUB 2. + Because there are so many signed GRUB 2 binaries in distribution, these + flaws threatened to overrun the standard Secure Boot storage for + forbidden binaries, known as the dbx. (The dbx is held in NVRAM, which + is severely limited in size.) Thus, Linux distribution maintainers + agreed to implement a dbx-like mechanism within Shim, and Microsoft + would add earlier Shim binaries to the UEFI dbx. The upshot of this is + that, if you installed rEFInd prior to these events, the Shim binary + that it uses may be added to your computer's dbx by Windows updates, and + rEFInd may stop working. The solution is to update the + shimx64.efi file in rEFInd's directory with the latest + version.
  • +

    If you launch a boot loader or other program from rEFInd that relies on the EFI's standard program-launching code, that program should take advantage of Shim and its MOKs. For instance, if you launch gummiboot (aka systemd-boot) from rEFInd (and rEFInd from Shim), gummiboot should be able to launch Shim/MOK-signed Linux kernels. In practice, this may not work with all versions of Shim, though. Shim 0.8 and later enables the binary that it launches to launch just one additional binary, not an endless stream of them. (rEFInd employs an internal workaround to this problem to do its own job.)

    @@ -540,7 +606,7 @@
    -

    copyright © 2012–2020 by Roderick W. Smith

    +

    copyright © 2012–2021 by Roderick W. Smith

    This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

    diff -Nru refind-0.12.0/docs/refind/sip.html refind-0.13.2/docs/refind/sip.html --- refind-0.12.0/docs/refind/sip.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/sip.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

    Originally written: 11/8/2015; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

    +3/13/2021, referencing rEFInd 0.13.2

    This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

    @@ -159,10 +159,10 @@ -

    Apple's macOS 10.11 (aka El Capitan) added a new feature, known as System Integrity Protection (SIP), aka "rootless" mode. This feature causes some consternation for advanced users, because it restricts what you can do with your computer, even as root. This page is dedicated to this new feature, including basic information on why SIP exists, how to install rEFInd on a computer with SIP enabled, and how to use rEFInd to manage SIP. Note that if you've come here for help installing rEFInd on a Mac with SIP enabled, you can click to one of the methods in the "Contents" box to the left of this paragraph. I recommend trying Recovery mode first; but if you have reason to try another method, you can do so.

    +

    Apple's macOS 10.11 (aka El Capitan) added a new feature, known as System Integrity Protection (SIP), aka "rootless" mode. This feature causes some consternation for advanced users, because it restricts what you can do with your computer, even as root. This page is dedicated to this feature, including basic information on why SIP exists, how to install rEFInd on a computer with SIP enabled, and how to use rEFInd to manage SIP. Note that if you've come here for help installing rEFInd on a Mac with SIP enabled, you can click to one of the methods in the "Contents" box to the left of this paragraph. I recommend trying Recovery mode first; but if you have reason to try another method, you can do so.

    -

    What Is SIP?

    +

    What Is SIP?

    To understand SIP, you should first know that Unix-like systems, including macOS, have traditionally provided a model of security in which ordinary users can read and write their own files (word processor documents, digital photos, etc.), but cannot write to system files (programs, system configuration files, etc.)—and users cannot even read some system files. This system security model has worked well for decades on traditional Unix systems, which have been administered by computer professionals and used by individuals with less experience. For administrative tasks, the root account is used. On Macs, this access is generally granted by the sudo command or by various GUI tools. Most Macs, in contrast to traditional Unix mainframes and minicomputers from the 20th century, are single-user computers that are administered by their users. Such people often lack the knowledge of the professional system administrators who have traditionally managed Unix systems; but they must still perform system administration tasks such as installing new software and configuring network settings. MacOS has always provided some measure of security by requiring users to enter their passwords before performing these dangerous tasks, and by providing GUI tools to help guide users through these tasks in a way that minimizes the risk of damage.

    @@ -271,9 +271,11 @@
  • You can use many Linux distributions' installers to run a minimal Linux system that you can use for installing rEFInd. This can be a useful trick even if you don't intend to run Linux normally. An Ubuntu image can be useful for this. You should insert the boot medium and hold down Option (or Alt) while booting to launch the installer, but be sure to pick the option to "try Ubuntu before installing" (or a similar option for other Linux distributions). You may need to install the efibootmgr package to install rEFInd. (Typing sudo apt-get install efibootmgr should do this in Ubuntu.)
  • +
  • For purposes of this discussion, rEFInd 0.12.0 and later count as "another OS." As described on the Installing rEFInd page (and in particular its Installing rEFInd from Within rEFInd section), rEFInd can install itself from an external boot medium. There are some caveats described in that section, though. Most importantly, rEFInd may take longer to appear than with other installation methods. If you intend to dual-boot macOS and Linux, you might start with this method and then re-install rEFInd (or use efibootmgr to create a fresh boot definition) within Linux; the boot entries created by efibootmgr don't seem to cause this problem, in my experience.
  • + -

    I've tested this method of installing rEFInd on my MacBook Air (purchased in late 2014) and on my first-generation 32-bit Mac Mini, but I can't promise it will work on all Macs—or even on a Mac that's identical to one of mine but with a configuration that's different from mine. This installation method has worked well for me, and reports I've seen recently suggest it works fine for many others, too. On the other hand, in the past (mid-2010s and earlier), this method was less reliable. I assume that improvements to Linux kernels and efibootmgr have rendered this method more reliable, so I recommend using the latest Linux (or other OS) version you can find if you want to use this method.

    +

    I've installed rEFInd from Linux and/or rEFInd itself on a 2017 iMac 18,2; on a 2014 MacBook Air 6,2; and on my first-generation 32-bit Mac Mini. I can't promise it will work on all Macs, though—or even on a Mac that's identical to one of mine but with a configuration that's different from mine. This installation method has worked well for me, and reports I've seen recently suggest it works fine for many others, too. On the other hand, in the past (mid-2010s and earlier), this method was less reliable. I assume that improvements to Linux kernels and efibootmgr have rendered this method more reliable, so I recommend using the latest Linux (or other OS) version you can find if you want to use this method.

    Using rEFInd to Manage SIP

    @@ -281,13 +283,13 @@

    Once rEFInd is installed, you can use it to manage SIP features; however, the rEFInd features needed to do this are disabled by default. You must uncomment or add two lines to your refind.conf file:

    - +
    • showtools—This line specifies tools that appear on the second row of icons in rEFInd. The new tool for managing SIP is called csr_rotate, so you must uncomment showtools and add this option, or create a new showtools line.
    • -
    • csr_values—This line lists the hexadecimal values through which you can rotate once csr_rotate is active on the showtools line. The trick to this token is selecting appropriate options. Several sites, such as this one, describe the meanings of the various options, but often not in much detail. Apple's own csrutil command sets values of 77 (disabled) or 10 (enabled). Note also that you specify hexadecimal values on this line, but without a leading 0x or other hexadecimal-notation indicator. If you specify gibberish values, or hexadecimal values higher than those used by SIP, rEFInd ignores the bad entries. Thus, if some of your values are being ignored, you should check your csr_values line for typos.
    • +
    • csr_values—This line lists the hexadecimal values through which you can rotate once csr_rotate is active on the showtools line. The trick to this token is selecting appropriate options. Several sites, such as this one, describe the meanings of the various options, but often not in much detail. Initially, Apple's own csrutil command sets values of 77 (disabled) or 10 (enabled); however, updates since the initial implementation have added more bits, as described in this Reddit thread, so now 877 (disabled) and 10 (enabled) are more common. Note also that you specify hexadecimal values on this line, but without a leading 0x or other hexadecimal-notation indicator. If you specify gibberish values, or hexadecimal values higher than those used by SIP, rEFInd ignores the bad entries. Thus, if some of your values are being ignored, you should check your csr_values line for typos.
    @@ -322,7 +324,7 @@
    -

    copyright © 2015–2020 by Roderick W. Smith

    +

    copyright © 2015–2021 by Roderick W. Smith

    This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

    diff -Nru refind-0.12.0/docs/refind/themes.html refind-0.13.2/docs/refind/themes.html --- refind-0.12.0/docs/refind/themes.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/themes.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

    Originally written: 4/19/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

    +3/13/2021, referencing rEFInd 0.13.2

    This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

    @@ -146,8 +146,10 @@ -

    rEFInd relies on both built-in and external graphical elements in its user interface, and all of these elements can be replaced by user-specified files. This fact makes rEFInd's "look and feel" highly adjustable even by non-programmers. This page will help you get started in making such changes to each of the major sets of features: banners and backgrounds, icons, icon selection backgrounds, and fonts. I conclude this page with pointers to a few themes that users have created for rEFInd.

    -

    Theming Basics

    +

    rEFInd relies on both built-in and external graphical elements in its user interface, and all of these elements can be replaced by user-specified files. This fact makes rEFInd's "look and feel" highly adjustable even by non-programmers. This page will help you get started in making such changes to each of the major sets of features: banners and backgrounds, icons, icon selection backgrounds, and fonts. I conclude this page with pointers to a few themes that users have created for rEFInd.

    + +
    +

    Theming Basics

    Broadly speaking, rEFInd's graphical elements fall into four categories:

    @@ -196,7 +198,7 @@

    If you want to use a full-screen background but also include the rEFInd logo, you can merge the two in a graphics editor by including the refind_banner-alpha.png or refind-banner.svg image from the banners subdirectory of the rEFInd package in your background.

    -

    It's possible to stretch or shrink any image to fill the screen. To do so, you should use the banner_scale option in refind.conf: Set it to noscale (the default) to use small banners as such or to crop larger images; or set it to fillscreen to adjust your banner's size to exactly fill the screen. This should be particularly handy for theme developers who want to use a full-screen background image, since you can now do this with just one image file.

    +

    It's possible to stretch or shrink any image to fill the screen. To do so, you should use the banner_scale option in refind.conf: Set it to noscale (the default) to use small banners as such or to crop larger images; or set it to fillscreen to adjust your banner's size to exactly fill the screen. This should be particularly handy for theme developers who want to use a full-screen background image, since you can do this with just one image file.

    Icons

    @@ -214,7 +216,7 @@ themes is that you might get bored with, or simply not like, the default graphics, so you can change them.

    -

    As described on various other pages of this document, rEFInd relies on icon files located in its icons subdirectory, and occasionally elsewhere, to define its overall appearance. You can adjust rEFInd's icons in a few ways:

    +

    As described on various other pages of this document, rEFInd relies on icon files located in its icons subdirectory, and occasionally elsewhere, to define its overall appearance. You can adjust rEFInd's icons in several ways, as detailed in the Setting OS Icons section of Configuring the Boot Manager. For creating a theme, though, the following techniques are most helpful:

      @@ -222,12 +224,6 @@
    • You can do as above, but place your new icons in the default icons subdirectory. This method is discouraged because using the refind-install script to upgrade rEFInd will replace your customized icons.
    • -
    • You can customize the appearance of an individual boot loader by placing an ICNS or PNG file in its directory with the same name as the boot loader but with a suitable image file extension, such as .icns or .png. For instance, if your boot loader program is elilo.efi, you can create a custom icon by naming it elilo.png.
    • - -
    • You can provide an icon for boot loaders stored in the root directory of a filesystem by placing a file called .VolumeIcon.icns, .VolumeIcon.png, .VolumeIcon.bmp, .VolumeIcon.jpg, or .VolumeIcon.jpeg in that volume's root.
    • - -
    • You can set a custom badge (the icon that identifies the disk type) by creating a file called .VolumeBadge.icns, .VolumeBadge.png, .VolumeBadge.bmp, .VolumeBadge.jpg, or .VolumeBadge.jpeg in that volume's root. This setting applies to all the boot loaders found on this volume, even if they're in subdirectories.
    • -
    • You can adjust the sizes of icons by using the big_icon_size and small_icon_size tokens in refind.conf. These tokens adjust the size of the first-row OS and second-row tool icons, respectively. The big_icon_size option also indirectly sets the disk-type badge size; badges have sides that are 1/4 the size of OS icons. The icons provided with rEFInd are 128x128 for OS icons, 48x48 for tools, and 32x32 for badges. The big_icon_size and small_icon_size tokens cause these icons to be scaled to the desired value; however, for best results you should replace the default icons with ones generated natively in the desired size. (All the image file formats supported by rEFInd are bitmap formats, and so the images will be degraded by scaling operations.)
    @@ -322,14 +318,14 @@
  • Zhu Qunying has created a Slackware-themed banner logo for rEFInd. Although it's not a full theme, I thought I'd mention it.
  • -
  • naymlezwun has created an macOS theme for rEFInd.
  • +
  • naymlezwun has created an OS X theme for rEFInd.

  • the rEFInd macOS theme uses Mac-like icons

    -
  • jamaladdeen on deviantART has created another macOS theme that resembles the macOS environment.
  • +
  • jamaladdeen on deviantART has created another OS X theme that resembles the macOS environment.

  • A theme that emulates the look of
     writing on a chalkboard

    - - -
  • The rEFInd-splash theme by AliciaTransmuted provides a swimming pool theme to the rEFInd main screen. See

  • +
  • HI-THEMES by + SLywnow is set of colorful themes with a tool for managing them. Most of + the themes are dominated by a single color, like the red one shown + here:
  • + +
    HI-THEMES provides several themes dominated by a single color

    + +
  • Papirus by + Duke93 uses distinctive icons against a dark background.
  • + +
    The Papirus theme uses distinctive icons against a dark background.

    @@ -437,7 +447,7 @@
    -

    copyright © 2012–2020 by Roderick W. Smith

    +

    copyright © 2012–2021 by Roderick W. Smith

    This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

    diff -Nru refind-0.12.0/docs/refind/todo.html refind-0.13.2/docs/refind/todo.html --- refind-0.12.0/docs/refind/todo.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/todo.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

    Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

    +3/13/2021, referencing rEFInd 0.13.2

    This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

    @@ -240,19 +240,17 @@ remission. Changes made in version 0.11.0 improve error reporting on Macs, which may help with this issue. -
  • Another Mac-specific display problem relates to "retina" displays: - Some users report that rEFInd comes up in a lower resolution than - the screen supports, and that this setting persists into the running - macOS instance, and can't be adjusted using the usual macOS means. - I've just recently (mid-2017) purchased an iMac with a "retina" - display, so I can now begin to investigate this problem, but as of - rEFInd 0.11.0, I haven't yet had a chance to look into it in depth. - I've heard that this problem affects not just rEFInd, but also GRUB, - and even Windows when dual-booting on a Mac; see On Macs with HiDPI (aka "Retina") displays, booting macOS via rEFInd + results in a lower resolution than booting macOS normally. + Similarly, booting any other OS results in sub-optimal resolution. + This problem is mitigated by the new (as of rEFInd 0.13.0) ability + to boot to a firmware-defined boot option; booting in this way + eliminates the problem for macOS, but not for Linux and probably not + for Windows. A better solution is desirable. See this YouTube video and This - AskUbuntu question and answers, for instance.
  • + AskUbuntu question and answers.
  • Some EFIs have bugs that cause the allegedly case-insensitive StriCmp() function to perform a case-sensitive comparison. @@ -413,10 +411,6 @@ to visually impaired users when rEFInd is ready to accept input would be helpful.
  • -
  • There's currently no way to create a manual boot stanza for a - BIOS-booted OS. This isn't a big priority for me personally, but I - can see how it could be for some people.
  • -
  • Improvements to the EFI drivers: @@ -455,8 +449,10 @@
  • rEFInd's support for network booting is primitive and relies on the external iPXE package. In my own testing, iPXE retrieves the - BIOS-mode boot loader from some servers that offer both, which - makes it useless on those networks.
  • + BIOS-mode boot loader from some servers that offer both, which makes + it useless on those networks. The reboot-to-firmware-boot-options + feature in rEFInd 0.13.0 at least partially addresses this + problem.
  • The handling of encrypted local Secure Boot keys by refind-install could be improved. Specifically, if you @@ -467,7 +463,8 @@ a rEFInd RPM or Debian package is installed, that installation may fail to set things up correctly.
  • -
  • A Mac-specific package is highly desirable.
  • +
  • A Mac-specific package is highly desirable, but seems impractical, + given SIP.
  • @@ -475,7 +472,7 @@
    -

    copyright © 2012–2020 by Roderick W. Smith

    +

    copyright © 2012–2021 by Roderick W. Smith

    This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

    diff -Nru refind-0.12.0/docs/refind/using.html refind-0.13.2/docs/refind/using.html --- refind-0.12.0/docs/refind/using.html 2020-03-13 12:46:26.000000000 +0000 +++ refind-0.13.2/docs/refind/using.html 2021-03-14 00:01:57.000000000 +0000 @@ -17,7 +17,7 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

    Originally written: 3/14/2012; last Web page update: -3/13/2020, referencing rEFInd 0.12.0

    +3/13/2021, referencing rEFInd 0.13.2

    This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!

    @@ -147,10 +147,10 @@

    For the most part, rEFInd is easy to use; just use your keyboard's arrow keys to select the OS you want to boot or the utility you want to launch and press the Enter key. A few details aren't entirely intuitive, though, so this page describes them.

    -

    Using Basic rEFInd Features

    +

    Using Basic rEFInd Features

    -

    With rEFInd in place and added to your firmware's list of boot utilities, you can reboot your computer. Depending on your configuration, rEFInd may come up immediately or you may need to select it from your firmware's boot options or reconfigure your firmware to present rEFInd automatically. Unfortunately, I can't offer much specific advice on this score, since EFI implementations differ so much in their user interfaces.

    +

    With rEFInd in place and added to your firmware's list of boot utilities, as described in Installing and Uninstalling rEFInd, you can reboot your computer. Depending on your configuration, rEFInd will probably come up immediately. If there was an installation problem or if your firmware is buggy, though, you may need to select it from your firmware's boot options or reconfigure your firmware to present rEFInd automatically. Unfortunately, I can't offer much specific advice on this score, since EFI implementations differ so much in their user interfaces. The Installing and Uninstalling rEFInd and Keeping rEFInd Booting pages offer some tips on common problems, though.

    Assuming rEFInd starts up correctly, you should see its main screen, which resembles the following:

    @@ -186,9 +186,9 @@ -

    By default, the options to display an information page, shutdown the computer, and reboot the computer are present. Options to launch a shell, launch gdisk, launch a memory test utility, launch the Apple recovery utility, launch the Windows recovery utility, and launch a Secure Boot key management utility will also appear automatically if these utilities are installed. A tag to reboot into the firmware appears if your firmware supports this feature. Options to launch the hybrid MBR tool (gptsync) and to exit from rEFInd are not displayed by default; you must edit the configuration file to enable these features, or to disable those that are displayed by default if you don't want them.

    +

    By default, options to display an information page, manage hidden tags, shutdown the computer, and reboot the computer are present. Options to launch a shell, launch gdisk, launch a memory test utility, launch the Apple recovery utility, launch the Windows recovery utility, and launch a Secure Boot key management utility will also appear automatically if these utilities are installed. A tag to reboot into the firmware appears if your firmware supports this feature. Options to launch the hybrid MBR tool (gptsync) and to exit from rEFInd are not displayed by default; you must edit the configuration file to enable these features, or to disable those that are displayed by default if you don't want them.

    -

    To launch an OS or utility, you should select its tag and then press the Enter key or the space bar.

    +

    The easiest way to launch an OS or utility is to select its tag and then press the Enter key or the space bar.

    @@ -338,7 +338,7 @@

    Sometimes it's necessary to boot a legacy (BIOS-based) OS on an EFI computer. This is especially true on Macs, since this has been the usual method of dual-booting macOS and Windows. (Since the release of Windows 8, though, booting Windows in EFI mode on Macs has become both more practical and more common.) In the past, many Linux distributions installed more easily in BIOS mode on Macs, but many Linux distributions now favor native EFI-mode installation on Macs. (See my EFI-Booting Ubuntu on a Mac page for an in-depth look at this topic.)

    -

    On UEFI-based PCs, booting some OSes in EFI mode and others in BIOS mode is less often necessary, since it's usually easy to install all your OSes in one mode or the other. If you have a working EFI-mode OS installation, though, and if you want to install an OS that lacks EFI-mode boot support, you may need to boot in both modes. Most of the BSDs (FreeBSD being a notable exception), Haiku, DOS, Windows XP and earlier, and many more obscure OSes still lack EFI support and so must be booted in BIOS mode. You might also want to boot a BIOS-mode emergency recovery CD, such as Parted Magic or System Rescue CD. Note, however, that EFI-mode support is being added to OSes. It's possible that some of those I've mentioned here will support EFI-mode booting by the time you read this!

    +

    On UEFI-based PCs, booting some OSes in EFI mode and others in BIOS mode is less often necessary, since it's usually easy to install all your OSes in one mode or the other. If you have a working EFI-mode OS installation, though, and if you want to install an OS that lacks EFI-mode boot support, you may need to boot in both modes. Most of the BSDs (FreeBSD being a notable exception), Haiku, DOS, Windows XP and earlier, and many more obscure OSes still lack EFI support and so must be booted in BIOS mode. You might also want to boot a BIOS-mode emergency recovery CD. Note, however, that EFI-mode support is being added to OSes. It's possible that some of those I've mentioned here will support EFI-mode booting by the time you read this!

    To help out when you need to boot in BIOS mode, rEFInd supports booting legacy OSes; however, the details vary between Macs and UEFI PCs. Also, be aware that some UEFI PCs lack the Compatibility Support Module (CSM) that's required for this feature to work. This is true even of some computers that can boot BIOS-based OSes natively. This can happen because the firmware is basically a BIOS with a UEFI implementation tacked on top of it; such systems rely on the native BIOS to boot in BIOS mode, and may not provide a way for EFI applications to access the BIOS features via CSM mechanisms. If you have such a computer and if you enable a legacy boot option in the configuration file, rEFInd notifies you of its inability to present legacy boot options when it starts up.

    @@ -378,13 +378,15 @@
  • Don't boot with removable media attached (unless you intend to boot from them)—If you insert an optical disc into your drive or plug in a removable device like a USB flash drive, rEFInd will attempt to scan it. This will slow down rEFInd's startup process, so you shouldn't make a habit of booting with such media inserted. In fact, there's another reason not to boot with external media attached: If such a medium is infected with malware, and if your firmware is configured to boot from external media first, you'll end up running the malware, possibly infecting your computer.
  • +
  • Adjust rEFInd installation options—Some Mac users have problems with a ~30-second delay before rEFInd's menu appears at all. The usual cause is a delay in Apple's EFI implementation that can be worked around by installing rEFInd in particular ways. See the Fixing Macintosh Boot Problems section of the Installing and Uninstalling rEFInd page for details.
  • +

    I hope these tips will help you to overcome any speed problems you're experiencing. As I said, rEFInd is reasonably fast on many computers, so you might not run into problems in the first place. If you do, though, reducing rEFInd's workload can help.


    -

    copyright © 2012–2020 by Roderick W. Smith

    +

    copyright © 2012–2021 by Roderick W. Smith

    This document is licensed under the terms of the GNU Free Documentation License (FDL), version 1.3.

    diff -Nru refind-0.12.0/docs/Styles/styles.css refind-0.13.2/docs/Styles/styles.css --- refind-0.12.0/docs/Styles/styles.css 2018-10-24 14:36:36.000000000 +0000 +++ refind-0.13.2/docs/Styles/styles.css 2021-02-21 21:40:57.000000000 +0000 @@ -1,6 +1,6 @@ body { - color:black; - background-color:white; +/* color:black; + background-color:white; */ text-align:left; /* font-family:"Georgia",serif; */ } @@ -19,6 +19,10 @@ font-family:"Bitstream Vera Sans", "Verdana", "Helvetica", "Arial", sans-serif; } +.clear { + clear: both; +} + .userinput { font-weight:bold; font-family:monospace; @@ -50,7 +54,12 @@ } .highlight { - background: #888888; + width: 95%; + float: center; + padding: 5px; + margin: 10px; + border: 5px solid gray; + background: #EEE; } .sidebar { diff -Nru refind-0.12.0/EfiLib/gnuefi-helper.h refind-0.13.2/EfiLib/gnuefi-helper.h --- refind-0.12.0/EfiLib/gnuefi-helper.h 2015-11-29 18:54:47.000000000 +0000 +++ refind-0.13.2/EfiLib/gnuefi-helper.h 2021-03-13 18:37:48.000000000 +0000 @@ -25,7 +25,6 @@ #include "efilib.h" #define EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH -#define UnicodeSPrint SPrint #define gRT RT #define gBS BS #ifndef CONST diff -Nru refind-0.12.0/EfiLib/legacy.c refind-0.13.2/EfiLib/legacy.c --- refind-0.12.0/EfiLib/legacy.c 2017-05-15 00:15:04.000000000 +0000 +++ refind-0.13.2/EfiLib/legacy.c 2021-03-13 18:25:32.000000000 +0000 @@ -28,6 +28,7 @@ #include "legacy.h" #include "GenericBdsLib.h" #include "../refind/global.h" +#include "../refind/lib.h" #include "../include/refit_call_wrapper.h" BOOT_OPTION_BBS_MAPPING *mBootOptionBbsMapping = NULL; @@ -181,9 +182,9 @@ // if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) { Fmt = L"%s %d"; - UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5); + SPrint(BootString, BufSize, Fmt, Type, Index - 5); } else { - UnicodeSPrint (BootString, BufSize, Fmt, Type); + SPrint(BootString, BufSize, Fmt, Type); } } @@ -274,7 +275,7 @@ // Loop all boot option from variable // for (Index = 0; Index < BootOptionNum; Index++) { - UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", (UINTN) BootOrder[Index]); + SPrint(BootOption, sizeof (BootOption), L"Boot%04x", (UINTN) BootOrder[Index]); BootOptionVar = BdsLibGetVariableAndSize ( BootOption, &EfiGlobalVariableGuid, @@ -379,7 +380,7 @@ CurrentBootOptionNo = (UINT16) ArrayIndex; } - UnicodeSPrint ( + SPrint ( BootString, sizeof (BootString), L"Boot%04x", @@ -460,15 +461,9 @@ Ptr += sizeof (BBS_TABLE); *((UINT16 *) Ptr) = (UINT16) Index; - - Status = refit_call5_wrapper(gRT->SetVariable, - BootString, - &EfiGlobalVariableGuid, - VAR_FLAG, - BufferSize, - Buffer - ); - + Status = EfivarSetRaw(&EfiGlobalVariableGuid, BootString, + (CHAR8*) Buffer, + BufferSize, TRUE); FreePool (Buffer); Buffer = NULL; @@ -816,13 +811,9 @@ ); if (BootOrderSize > 0) { - Status = refit_call5_wrapper(gRT->SetVariable, - L"BootOrder", - &EfiGlobalVariableGuid, - VAR_FLAG, - BootOrderSize, - BootOrder - ); + Status = EfivarSetRaw(&EfiGlobalVariableGuid, L"BootOrder", + (CHAR8*) BootOrder, + BootOrderSize, TRUE); } else { EfiLibDeleteVariable (L"BootOrder", &EfiGlobalVariableGuid); } @@ -861,7 +852,7 @@ Status = EFI_SUCCESS; Index2Del = 0; - UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionNumber); + SPrint (BootOption, sizeof (BootOption), L"Boot%04x", OptionNumber); Status = EfiLibDeleteVariable (BootOption, &EfiGlobalVariableGuid); // @@ -950,7 +941,7 @@ Index = 0; while (Index < BootOrderSize / sizeof (UINT16)) { - UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); + SPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]); BootOptionVar = BdsLibGetVariableAndSize ( BootOption, &EfiGlobalVariableGuid, @@ -1034,13 +1025,9 @@ // Adjust the number of boot options. // if (BootOrderSize != 0) { - Status = refit_call5_wrapper(gRT->SetVariable, - L"BootOrder", - &EfiGlobalVariableGuid, - VAR_FLAG, - BootOrderSize, - BootOrder - ); + Status = EfivarSetRaw(&EfiGlobalVariableGuid, L"BootOrder", + (CHAR8*) BootOrder, + BootOrderSize, TRUE); } else { EfiLibDeleteVariable (L"BootOrder", &EfiGlobalVariableGuid); } diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorer.c refind-0.13.2/filesystems/FileExplorerLib/FileExplorer.c --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorer.c 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorer.c 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,1658 @@ +/** @file +File explorer related functions. + +Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#include "FileExplorer.h" + +EFI_GUID FileExplorerGuid = EFI_FILE_EXPLORE_FORMSET_GUID; + +/// +/// File system selection menu +/// +MENU_OPTION mFsOptionMenu = { + MENU_OPTION_SIGNATURE, + {NULL}, + 0, + FALSE +}; + +FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate = { + FILE_EXPLORER_CALLBACK_DATA_SIGNATURE, + NULL, + NULL, + { + LibExtractConfig, + LibRouteConfig, + LibCallback + }, + NULL, + &mFsOptionMenu, + 0 +}; + +HII_VENDOR_DEVICE_PATH *gHiiVendorDevicePath; + +HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + // + // Will be replace with gEfiCallerIdGuid in code. + // + { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } } + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +VOID *mLibStartOpCodeHandle = NULL; +VOID *mLibEndOpCodeHandle = NULL; +EFI_IFR_GUID_LABEL *mLibStartLabel = NULL; +EFI_IFR_GUID_LABEL *mLibEndLabel = NULL; +UINT16 mQuestionIdUpdate; +CHAR16 mNewFileName[MAX_FILE_NAME_LEN]; +CHAR16 mNewFolderName[MAX_FOLDER_NAME_LEN]; +UINTN mNewFileQuestionId = NEW_FILE_QUESTION_ID_BASE; +UINTN mNewFolderQuestionId = NEW_FOLDER_QUESTION_ID_BASE; + +/** + Create a new file or folder in current directory. + + @param FileName Point to the fileNmae or folder. + @param CreateFile CreateFile== TRUE means create a new file. + CreateFile== FALSE means create a new Folder. + +**/ +EFI_STATUS +LibCreateNewFile ( + IN CHAR16 *FileName, + IN BOOLEAN CreateFile + ); + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request A null-terminated Unicode string in format. + @param Progress On return, points to a character in the Request string. + Points to the string's null terminator if request was successful. + Points to the most recent '&' before the first failing name/value + pair (or the beginning of the string if the failure is in the + first name/value pair) if the request was not successful. + @param Results A null-terminated Unicode string in format which + has all values filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +LibExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + return EFI_NOT_FOUND; +} + +/** + This function processes the results of changes in configuration. + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration A null-terminated Unicode string in format. + @param Progress A pointer to a string filled in with the offset of the most + recent '&' before the first failing name/value pair (or the + beginning of the string if the failure is in the first + name/value pair) or the terminating NULL if all was successful. + + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +LibRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + return EFI_NOT_FOUND; +} + +/** + This function processes the results of changes in configuration. + When user select a interactive opcode, this callback will be triggered. + Based on the Question(QuestionId) that triggers the callback, the corresponding + actions is performed. It handles: + + 1) Process the axtra action or exit file explorer when user select one file . + 2) update of file content if a dir is selected. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original exporting driver + so that it can identify the type of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original exporting driver. + @param ActionRequest On return, points to the action requested by the callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval other error Error occur when parse one directory. +**/ +EFI_STATUS +EFIAPI +LibCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_STATUS Status; + BOOLEAN NeedExit; + CHAR16 *NewFileName; + CHAR16 *NewFolderName; + + NeedExit = TRUE; + NewFileName = NULL; + NewFolderName = NULL; + + if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) { + // + // Do nothing for other UEFI Action. Only do call back when data is changed. + // + return EFI_UNSUPPORTED; + } + + if (Action == EFI_BROWSER_ACTION_CHANGED) { + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (QuestionId == KEY_VALUE_CREATE_FILE_AND_EXIT) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + if (!IsZeroBuffer (mNewFileName, sizeof (mNewFileName))) { + Status = LibCreateNewFile (mNewFileName,TRUE); + ZeroMem (mNewFileName,sizeof (mNewFileName)); + } + } + + if (QuestionId == KEY_VALUE_NO_CREATE_FILE_AND_EXIT) { + ZeroMem (mNewFileName,sizeof (mNewFileName)); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } + + if (QuestionId == KEY_VALUE_CREATE_FOLDER_AND_EXIT) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + if (!IsZeroBuffer (mNewFolderName, sizeof (mNewFolderName))) { + Status = LibCreateNewFile (mNewFolderName, FALSE); + ZeroMem (mNewFolderName,sizeof (mNewFolderName)); + } + } + + if (QuestionId == KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT) { + ZeroMem (mNewFolderName,sizeof (mNewFolderName)); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } + + if (QuestionId == NEW_FILE_NAME_ID) { + NewFileName = HiiGetString (gFileExplorerPrivate.FeHiiHandle, Value->string, NULL); + if (NewFileName != NULL) { + StrCpyS (mNewFileName, MAX_FILE_NAME_LEN, NewFileName); + FreePool (NewFileName); + NewFileName = NULL; + } else { + return EFI_INVALID_PARAMETER; + } + } + + if (QuestionId == NEW_FOLDER_NAME_ID) { + NewFolderName = HiiGetString (gFileExplorerPrivate.FeHiiHandle, Value->string, NULL); + if (NewFolderName != NULL) { + StrCpyS (mNewFolderName, MAX_FOLDER_NAME_LEN, NewFolderName); + FreePool (NewFolderName); + NewFolderName = NULL; + } else { + return EFI_INVALID_PARAMETER; + } + } + + if (QuestionId >= FILE_OPTION_OFFSET) { + LibGetDevicePath(QuestionId); + + // + // Process the extra action. + // + if (gFileExplorerPrivate.ChooseHandler != NULL) { + NeedExit = gFileExplorerPrivate.ChooseHandler (gFileExplorerPrivate.RetDevicePath); + } + + if (NeedExit) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } + } + } else if (Action == EFI_BROWSER_ACTION_CHANGING) { + if (Value == NULL) { + return EFI_INVALID_PARAMETER; + } + if (QuestionId >= FILE_OPTION_OFFSET) { + LibGetDevicePath(QuestionId); + Status = LibUpdateFileExplorer (QuestionId); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + return EFI_SUCCESS; +} + +/** + Create a menu entry by given menu type. + + @retval NULL If failed to create the menu. + @return the new menu entry. + +**/ +MENU_ENTRY * +LibCreateMenuEntry ( + VOID + ) +{ + MENU_ENTRY *MenuEntry; + + // + // Create new menu entry + // + MenuEntry = AllocateZeroPool (sizeof (MENU_ENTRY)); + if (MenuEntry == NULL) { + return NULL; + } + + MenuEntry->VariableContext = AllocateZeroPool (sizeof (FILE_CONTEXT)); + if (MenuEntry->VariableContext == NULL) { + FreePool (MenuEntry); + return NULL; + } + + MenuEntry->Signature = MENU_ENTRY_SIGNATURE; + return MenuEntry; +} + + +/** + Get the Menu Entry from the list in Menu Entry List. + + If MenuNumber is great or equal to the number of Menu + Entry in the list, then ASSERT. + + @param MenuOption The Menu Entry List to read the menu entry. + @param MenuNumber The index of Menu Entry. + + @return The Menu Entry. + +**/ +MENU_ENTRY * +LibGetMenuEntry ( + MENU_OPTION *MenuOption, + UINTN MenuNumber + ) +{ + MENU_ENTRY *NewMenuEntry; + UINTN Index; + LIST_ENTRY *List; + + ASSERT (MenuNumber < MenuOption->MenuNumber); + + List = MenuOption->Head.ForwardLink; + for (Index = 0; Index < MenuNumber; Index++) { + List = List->ForwardLink; + } + + NewMenuEntry = CR (List, MENU_ENTRY, Link, MENU_ENTRY_SIGNATURE); + + return NewMenuEntry; +} + +/** + Free up all resource allocated for a BM_MENU_ENTRY. + + @param MenuEntry A pointer to BM_MENU_ENTRY. + +**/ +VOID +LibDestroyMenuEntry ( + MENU_ENTRY *MenuEntry + ) +{ + FILE_CONTEXT *FileContext; + + FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext; + + if (!FileContext->IsRoot) { + if (FileContext->DevicePath != NULL) { + FreePool (FileContext->DevicePath); + } + } else { + if (FileContext->FileHandle != NULL) { + FileContext->FileHandle->Close (FileContext->FileHandle); + } + } + + if (FileContext->FileName != NULL) { + FreePool (FileContext->FileName); + } + + FreePool (FileContext); + + if (MenuEntry->DisplayString != NULL) { + FreePool (MenuEntry->DisplayString); + } + if (MenuEntry->HelpString != NULL) { + FreePool (MenuEntry->HelpString); + } + + FreePool (MenuEntry); +} + + +/** + Free resources allocated in Allocate Rountine. + + @param FreeMenu Menu to be freed +**/ +VOID +LibFreeMenu ( + MENU_OPTION *FreeMenu + ) +{ + MENU_ENTRY *MenuEntry; + while (!IsListEmpty (&FreeMenu->Head)) { + MenuEntry = CR ( + FreeMenu->Head.ForwardLink, + MENU_ENTRY, + Link, + MENU_ENTRY_SIGNATURE + ); + RemoveEntryList (&MenuEntry->Link); + LibDestroyMenuEntry (MenuEntry); + } + FreeMenu->MenuNumber = 0; +} + +/** + + Function opens and returns a file handle to the root directory of a volume. + + @param DeviceHandle A handle for a device + + @return A valid file handle or NULL is returned + +**/ +EFI_FILE_HANDLE +LibOpenRoot ( + IN EFI_HANDLE DeviceHandle + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; + EFI_FILE_HANDLE File; + + File = NULL; + + // + // File the file system interface to the device + // + Status = gBS->HandleProtocol ( + DeviceHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID *) &Volume + ); + + // + // Open the root directory of the volume + // + if (!EFI_ERROR (Status)) { + Status = Volume->OpenVolume ( + Volume, + &File + ); + } + // + // Done + // + return EFI_ERROR (Status) ? NULL : File; +} + +/** + This function converts an input device structure to a Unicode string. + + @param DevPath A pointer to the device path structure. + + @return A new allocated Unicode string that represents the device path. + +**/ +CHAR16 * +LibDevicePathToStr ( + IN EFI_DEVICE_PATH_PROTOCOL *DevPath + ) +{ + EFI_STATUS Status; + CHAR16 *ToText; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText; + + if (DevPath == NULL) { + return NULL; + } + + Status = gBS->LocateProtocol ( + &gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID **) &DevPathToText + ); + ASSERT_EFI_ERROR (Status); + ToText = DevPathToText->ConvertDevicePathToText ( + DevPath, + FALSE, + TRUE + ); + ASSERT (ToText != NULL); + + return ToText; +} + +/** + Duplicate a string. + + @param Src The source. + + @return A new string which is duplicated copy of the source. + @retval NULL If there is not enough memory. + +**/ +CHAR16 * +LibStrDuplicate ( + IN CHAR16 *Src + ) +{ + CHAR16 *Dest; + UINTN Size; + + Size = StrSize (Src); + Dest = AllocateZeroPool (Size); + ASSERT (Dest != NULL); + if (Dest != NULL) { + CopyMem (Dest, Src, Size); + } + + return Dest; +} + +/** + + Function gets the file information from an open file descriptor, and stores it + in a buffer allocated from pool. + + @param FHand File Handle. + @param InfoType Info type need to get. + + @retval A pointer to a buffer with file information or NULL is returned + +**/ +VOID * +LibFileInfo ( + IN EFI_FILE_HANDLE FHand, + IN EFI_GUID *InfoType + ) +{ + EFI_STATUS Status; + EFI_FILE_INFO *Buffer; + UINTN BufferSize; + + Buffer = NULL; + BufferSize = 0; + + Status = FHand->GetInfo ( + FHand, + InfoType, + &BufferSize, + Buffer + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Buffer = AllocatePool (BufferSize); + ASSERT (Buffer != NULL); + } + + Status = FHand->GetInfo ( + FHand, + InfoType, + &BufferSize, + Buffer + ); + + return Buffer; +} + +/** + + Get file type base on the file name. + Just cut the file name, from the ".". eg ".efi" + + @param FileName File need to be checked. + + @retval the file type string. + +**/ +CHAR16* +LibGetTypeFromName ( + IN CHAR16 *FileName + ) +{ + UINTN Index; + + Index = StrLen (FileName) - 1; + while ((FileName[Index] != L'.') && (Index != 0)) { + Index--; + } + + return Index == 0 ? NULL : &FileName[Index]; +} + +/** + Converts the unicode character of the string from uppercase to lowercase. + This is a internal function. + + @param ConfigString String to be converted + +**/ +VOID +LibToLowerString ( + IN CHAR16 *String + ) +{ + CHAR16 *TmpStr; + + for (TmpStr = String; *TmpStr != L'\0'; TmpStr++) { + if (*TmpStr >= L'A' && *TmpStr <= L'Z') { + *TmpStr = (CHAR16) (*TmpStr - L'A' + L'a'); + } + } +} + +/** + + Check whether current FileName point to a valid + Efi Image File. + + @param FileName File need to be checked. + + @retval TRUE Is Efi Image + @retval FALSE Not a valid Efi Image + +**/ +BOOLEAN +LibIsSupportedFileType ( + IN UINT16 *FileName + ) +{ + CHAR16 *InputFileType; + CHAR16 *TmpStr; + BOOLEAN IsSupported; + + if (gFileExplorerPrivate.FileType == NULL) { + return TRUE; + } + + InputFileType = LibGetTypeFromName (FileName); + // + // If the file not has *.* style, always return TRUE. + // + if (InputFileType == NULL) { + return TRUE; + } + + TmpStr = AllocateCopyPool (StrSize (InputFileType), InputFileType); + ASSERT(TmpStr != NULL); + LibToLowerString(TmpStr); + + IsSupported = (StrStr (gFileExplorerPrivate.FileType, TmpStr) == NULL ? FALSE : TRUE); + + FreePool (TmpStr); + return IsSupported; +} + +/** + + Append file name to existing file name. + + @param Str1 The existing file name + @param Str2 The file name to be appended + + @return Allocate a new string to hold the appended result. + Caller is responsible to free the returned string. + +**/ +CHAR16 * +LibAppendFileName ( + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ) +{ + UINTN Size1; + UINTN Size2; + UINTN MaxLen; + CHAR16 *Str; + CHAR16 *TmpStr; + CHAR16 *Ptr; + CHAR16 *LastSlash; + + Size1 = StrSize (Str1); + Size2 = StrSize (Str2); + + // + // Check overflow + // + if (((MAX_UINTN - Size1) < Size2) || ((MAX_UINTN - Size1 - Size2) < sizeof(CHAR16))) { + return NULL; + } + + MaxLen = (Size1 + Size2 + sizeof (CHAR16))/ sizeof (CHAR16); + Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16)); + ASSERT (Str != NULL); + + TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16)); + ASSERT (TmpStr != NULL); + + StrCpyS (Str, MaxLen, Str1); + if (!((*Str == '\\') && (*(Str + 1) == 0))) { + StrCatS (Str, MaxLen, L"\\"); + } + + StrCatS (Str, MaxLen, Str2); + + Ptr = Str; + LastSlash = Str; + while (*Ptr != 0) { + if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') { + // + // Convert "\Name\..\" to "\" + // DO NOT convert the .. if it is at the end of the string. This will + // break the .. behavior in changing directories. + // + + // + // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings + // that overlap. + // + StrCpyS (TmpStr, MaxLen, Ptr + 3); + StrCpyS (LastSlash, MaxLen - ((UINTN) LastSlash - (UINTN) Str) / sizeof (CHAR16), TmpStr); + Ptr = LastSlash; + } else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') { + // + // Convert a "\.\" to a "\" + // + + // + // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings + // that overlap. + // + StrCpyS (TmpStr, MaxLen, Ptr + 2); + StrCpyS (Ptr, MaxLen - ((UINTN) Ptr - (UINTN) Str) / sizeof (CHAR16), TmpStr); + Ptr = LastSlash; + } else if (*Ptr == '\\') { + LastSlash = Ptr; + } + + Ptr++; + } + + FreePool (TmpStr); + + return Str; +} + +/** + This function build the FsOptionMenu list which records all + available file system in the system. They includes all instances + of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM. + + + @retval EFI_SUCCESS Success find the file system + @retval EFI_OUT_OF_RESOURCES Can not create menu entry + +**/ +EFI_STATUS +LibFindFileSystem ( + VOID + ) +{ + UINTN NoSimpleFsHandles; + EFI_HANDLE *SimpleFsHandle; + UINT16 *VolumeLabel; + UINTN Index; + EFI_STATUS Status; + MENU_ENTRY *MenuEntry; + FILE_CONTEXT *FileContext; + UINTN OptionNumber; + EFI_FILE_SYSTEM_VOLUME_LABEL *Info; + + NoSimpleFsHandles = 0; + OptionNumber = 0; + + // + // Locate Handles that support Simple File System protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NoSimpleFsHandles, + &SimpleFsHandle + ); + if (!EFI_ERROR (Status)) { + // + // Find all the instances of the File System prototocol + // + for (Index = 0; Index < NoSimpleFsHandles; Index++) { + // + // Allocate pool for this load option + // + MenuEntry = LibCreateMenuEntry (); + if (NULL == MenuEntry) { + FreePool (SimpleFsHandle); + return EFI_OUT_OF_RESOURCES; + } + + FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext; + FileContext->DeviceHandle = SimpleFsHandle[Index]; + FileContext->FileHandle = LibOpenRoot (FileContext->DeviceHandle); + if (FileContext->FileHandle == NULL) { + LibDestroyMenuEntry (MenuEntry); + continue; + } + + MenuEntry->HelpString = LibDevicePathToStr (DevicePathFromHandle (FileContext->DeviceHandle)); + FileContext->FileName = LibStrDuplicate (L"\\"); + FileContext->DevicePath = FileDevicePath (FileContext->DeviceHandle, FileContext->FileName); + FileContext->IsDir = TRUE; + FileContext->IsRoot = TRUE; + + // + // Get current file system's Volume Label + // + Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *) LibFileInfo (FileContext->FileHandle, &gEfiFileSystemVolumeLabelInfoIdGuid); + if (Info == NULL) { + VolumeLabel = L"NO FILE SYSTEM INFO"; + } else { + if (Info->VolumeLabel == NULL) { + VolumeLabel = L"NULL VOLUME LABEL"; + } else { + VolumeLabel = Info->VolumeLabel; + if (*VolumeLabel == 0x0000) { + VolumeLabel = L"NO VOLUME LABEL"; + } + } + } + MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR); + ASSERT (MenuEntry->DisplayString != NULL); + UnicodeSPrint ( + MenuEntry->DisplayString, + MAX_CHAR, + L"%s, [%s]", + VolumeLabel, + MenuEntry->HelpString + ); + MenuEntry->DisplayStringToken = HiiSetString ( + gFileExplorerPrivate.FeHiiHandle, + 0, + MenuEntry->DisplayString, + NULL + ); + + if (Info != NULL) + FreePool (Info); + + OptionNumber++; + InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &MenuEntry->Link); + } + } + + if (NoSimpleFsHandles != 0) { + FreePool (SimpleFsHandle); + } + + gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber; + + return EFI_SUCCESS; +} + +/** + Find the file handle from the input menu info. + + @param MenuEntry Input Menu info. + @param RetFileHandle Return the file handle for the input device path. + + @retval EFI_SUCESS Find the file handle success. + @retval Other Find the file handle failure. +**/ +EFI_STATUS +LibGetFileHandleFromMenu ( + IN MENU_ENTRY *MenuEntry, + OUT EFI_FILE_HANDLE *RetFileHandle + ) +{ + EFI_FILE_HANDLE Dir; + EFI_FILE_HANDLE NewDir; + FILE_CONTEXT *FileContext; + EFI_STATUS Status; + + FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext; + Dir = FileContext->FileHandle; + + // + // Open current directory to get files from it + // + Status = Dir->Open ( + Dir, + &NewDir, + FileContext->FileName, + EFI_FILE_READ_ONLY, + 0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!FileContext->IsRoot) { + Dir->Close (Dir); + } + + *RetFileHandle = NewDir; + + return EFI_SUCCESS; +} + +/** + Find the file handle from the input device path info. + + @param RootDirectory Device path info. + @param RetFileHandle Return the file handle for the input device path. + @param ParentFileName Parent file name. + @param DeviceHandle Driver handle for this partition. + + @retval EFI_SUCESS Find the file handle success. + @retval Other Find the file handle failure. +**/ +EFI_STATUS +LibGetFileHandleFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory, + OUT EFI_FILE_HANDLE *RetFileHandle, + OUT UINT16 **ParentFileName, + OUT EFI_HANDLE *DeviceHandle + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode; + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; + EFI_FILE_HANDLE FileHandle; + EFI_FILE_HANDLE LastHandle; + CHAR16 *TempPath; + + *ParentFileName = NULL; + + // + // Attempt to access the file via a file system interface + // + DevicePathNode = RootDirectory; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathNode, &Handle); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Volume); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the Volume to get the File System handle + // + Status = Volume->OpenVolume (Volume, &FileHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + *DeviceHandle = Handle; + + if (IsDevicePathEnd(DevicePathNode)) { + *ParentFileName = AllocateCopyPool (StrSize (L"\\"), L"\\"); + *RetFileHandle = FileHandle; + return EFI_SUCCESS; + } + + // + // Duplicate the device path to avoid the access to unaligned device path node. + // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH + // nodes, It assures the fields in device path nodes are 2 byte aligned. + // + TempDevicePathNode = DuplicateDevicePath (DevicePathNode); + if (TempDevicePathNode == NULL) { + + // + // Setting Status to an EFI_ERROR value will cause the rest of + // the file system support below to be skipped. + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the + // directory information and filename can be seperate. The goal is to inch + // our way down each device path node and close the previous node + // + DevicePathNode = TempDevicePathNode; + while (!EFI_ERROR (Status) && !IsDevicePathEnd (DevicePathNode)) { + if (DevicePathType (DevicePathNode) != MEDIA_DEVICE_PATH || + DevicePathSubType (DevicePathNode) != MEDIA_FILEPATH_DP) { + Status = EFI_UNSUPPORTED; + goto Done; + } + + LastHandle = FileHandle; + FileHandle = NULL; + + Status = LastHandle->Open ( + LastHandle, + &FileHandle, + ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName, + EFI_FILE_MODE_READ, + 0 + ); + if (*ParentFileName == NULL) { + *ParentFileName = AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName), ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName); + } else { + TempPath = LibAppendFileName (*ParentFileName, ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName); + if (TempPath == NULL) { + LastHandle->Close (LastHandle); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + FreePool (*ParentFileName); + *ParentFileName = TempPath; + } + + // + // Close the previous node + // + LastHandle->Close (LastHandle); + + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + + if (EFI_ERROR (Status)) { + goto Done; + } + + *RetFileHandle = FileHandle; + + Status = EFI_SUCCESS; + +Done: + if (TempDevicePathNode != NULL) { + FreePool (TempDevicePathNode); + } + + if ((FileHandle != NULL) && (EFI_ERROR (Status))) { + FileHandle->Close (FileHandle); + } + + return Status; +} + +/** + Create a new file or folder in current directory. + + @param FileName Point to the fileNmae or folder name. + @param CreateFile CreateFile== TRUE means create a new file. + CreateFile== FALSE means create a new Folder. + +**/ +EFI_STATUS +LibCreateNewFile ( + IN CHAR16 *FileName, + IN BOOLEAN CreateFile + ) +{ + EFI_FILE_HANDLE FileHandle; + EFI_FILE_HANDLE NewHandle; + EFI_HANDLE DeviceHandle; + EFI_STATUS Status; + CHAR16 *ParentName; + CHAR16 *FullFileName; + + NewHandle = NULL; + FullFileName = NULL; + + LibGetFileHandleFromDevicePath(gFileExplorerPrivate.RetDevicePath, &FileHandle, &ParentName, &DeviceHandle); + FullFileName = LibAppendFileName (ParentName, FileName); + if (FullFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + if (CreateFile) { + Status = FileHandle->Open( + FileHandle, + &NewHandle, + FullFileName, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE| EFI_FILE_MODE_CREATE, + 0 + ); + if (EFI_ERROR (Status)) { + FileHandle->Close (FileHandle); + return Status; + } + } else { + Status = FileHandle->Open( + FileHandle, + &NewHandle, + FullFileName, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE| EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY + ); + if (EFI_ERROR (Status)) { + FileHandle->Close (FileHandle); + return Status; + } + } + + FileHandle->Close (FileHandle); + + // + // Return the DevicePath of the new created file or folder. + // + gFileExplorerPrivate.RetDevicePath = FileDevicePath (DeviceHandle, FullFileName); + + return EFI_SUCCESS; + +} + +/** + Find files under current directory. + + All files and sub-directories in current directory + will be stored in DirectoryMenu for future use. + + @param FileHandle Parent file handle. + @param FileName Parent file name. + @param DeviceHandle Driver handle for this partition. + + @retval EFI_SUCCESS Get files from current dir successfully. + @return Other value if can't get files from current dir. + +**/ +EFI_STATUS +LibFindFiles ( + IN EFI_FILE_HANDLE FileHandle, + IN UINT16 *FileName, + IN EFI_HANDLE DeviceHandle + ) +{ + EFI_FILE_INFO *DirInfo; + UINTN BufferSize; + UINTN DirBufferSize; + MENU_ENTRY *NewMenuEntry; + FILE_CONTEXT *NewFileContext; + UINTN Pass; + EFI_STATUS Status; + UINTN OptionNumber; + + OptionNumber = 0; + + DirBufferSize = sizeof (EFI_FILE_INFO) + 1024; + DirInfo = AllocateZeroPool (DirBufferSize); + if (DirInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get all files in current directory + // Pass 1 to get Directories + // Pass 2 to get files that are EFI images + // + Status = EFI_SUCCESS; + for (Pass = 1; Pass <= 2; Pass++) { + FileHandle->SetPosition (FileHandle, 0); + for (;;) { + BufferSize = DirBufferSize; + Status = FileHandle->Read (FileHandle, &BufferSize, DirInfo); + if (EFI_ERROR (Status) || BufferSize == 0) { + Status = EFI_SUCCESS; + break; + } + + if (((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 && Pass == 2) || + ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && Pass == 1) + ) { + // + // Pass 1 is for Directories + // Pass 2 is for file names + // + continue; + } + + if (!((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 || LibIsSupportedFileType (DirInfo->FileName))) { + // + // Slip file unless it is a directory entry or a .EFI file + // + continue; + } + + NewMenuEntry = LibCreateMenuEntry (); + if (NULL == NewMenuEntry) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext; + NewFileContext->DeviceHandle = DeviceHandle; + NewFileContext->FileName = LibAppendFileName (FileName, DirInfo->FileName); + if (NewFileContext->FileName == NULL) { + LibDestroyMenuEntry (NewMenuEntry); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + NewFileContext->FileHandle = FileHandle; + NewFileContext->DevicePath = FileDevicePath (NewFileContext->DeviceHandle, NewFileContext->FileName); + NewMenuEntry->HelpString = NULL; + NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY); + + if (NewFileContext->IsDir) { + BufferSize = StrLen (DirInfo->FileName) * 2 + 6; + NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize); + UnicodeSPrint ( + NewMenuEntry->DisplayString, + BufferSize, + L"<%s>", + DirInfo->FileName + ); + } else { + NewMenuEntry->DisplayString = LibStrDuplicate (DirInfo->FileName); + } + + NewMenuEntry->DisplayStringToken = HiiSetString ( + gFileExplorerPrivate.FeHiiHandle, + 0, + NewMenuEntry->DisplayString, + NULL + ); + + NewFileContext->IsRoot = FALSE; + + OptionNumber++; + InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &NewMenuEntry->Link); + } + } + + gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber; + +Done: + + FreePool (DirInfo); + + return Status; +} + +/** + Refresh the global UpdateData structure. + +**/ +VOID +LibRefreshUpdateData ( + VOID + ) +{ + // + // Free current updated date + // + if (mLibStartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (mLibStartOpCodeHandle); + } + if (mLibEndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (mLibEndOpCodeHandle); + } + + // + // Create new OpCode Handle + // + mLibStartOpCodeHandle = HiiAllocateOpCodeHandle (); + mLibEndOpCodeHandle = HiiAllocateOpCodeHandle (); + + // + // Create Hii Extend Label OpCode as the start opcode + // + mLibStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + mLibStartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + mLibStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + + mLibStartLabel->Number = FORM_FILE_EXPLORER_ID; + + // + // Create Hii Extend Label OpCode as the start opcode + // + mLibEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + mLibEndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + mLibEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + + mLibEndLabel->Number = LABEL_END; +} + +/** + + Update the File Explore page. + +**/ +VOID +LibUpdateFileExplorePage ( + VOID + ) +{ + UINTN Index; + MENU_ENTRY *NewMenuEntry; + FILE_CONTEXT *NewFileContext; + MENU_OPTION *MenuOption; + BOOLEAN CreateNewFile; + + NewMenuEntry = NULL; + NewFileContext = NULL; + CreateNewFile = FALSE; + + LibRefreshUpdateData (); + MenuOption = gFileExplorerPrivate.FsOptionMenu; + + mQuestionIdUpdate += QUESTION_ID_UPDATE_STEP; + + for (Index = 0; Index < MenuOption->MenuNumber; Index++) { + NewMenuEntry = LibGetMenuEntry (MenuOption, Index); + NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext; + + if (!NewFileContext->IsRoot && !CreateNewFile) { + HiiCreateGotoOpCode ( + mLibStartOpCodeHandle, + FORM_ADD_NEW_FILE_ID, + STRING_TOKEN (STR_NEW_FILE), + STRING_TOKEN (STR_NEW_FILE_HELP), + EFI_IFR_FLAG_CALLBACK, + (UINT16) (mNewFileQuestionId++) + ); + HiiCreateGotoOpCode ( + mLibStartOpCodeHandle, + FORM_ADD_NEW_FOLDER_ID, + STRING_TOKEN (STR_NEW_FOLDER), + STRING_TOKEN (STR_NEW_FOLDER_HELP), + EFI_IFR_FLAG_CALLBACK, + (UINT16) (mNewFolderQuestionId++) + ); + HiiCreateTextOpCode( + mLibStartOpCodeHandle, + STRING_TOKEN (STR_NULL_STRING), + STRING_TOKEN (STR_NULL_STRING), + 0 + ); + CreateNewFile = TRUE; + } + + if (!NewFileContext->IsDir) { + // + // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile. + // + HiiCreateActionOpCode ( + mLibStartOpCodeHandle, + (UINT16) (FILE_OPTION_OFFSET + Index + mQuestionIdUpdate), + NewMenuEntry->DisplayStringToken, + STRING_TOKEN (STR_NULL_STRING), + EFI_IFR_FLAG_CALLBACK, + 0 + ); + } else { + // + // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState. + // + HiiCreateGotoOpCode ( + mLibStartOpCodeHandle, + FORM_FILE_EXPLORER_ID, + NewMenuEntry->DisplayStringToken, + STRING_TOKEN (STR_NULL_STRING), + EFI_IFR_FLAG_CALLBACK, + (UINT16) (FILE_OPTION_OFFSET + Index + mQuestionIdUpdate) + ); + } + } + + HiiUpdateForm ( + gFileExplorerPrivate.FeHiiHandle, + &FileExplorerGuid, + FORM_FILE_EXPLORER_ID, + mLibStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID + mLibEndOpCodeHandle // LABEL_END + ); +} + +/** + Update the file explower page with the refershed file system. + + @param KeyValue Key value to identify the type of data to expect. + + @retval EFI_SUCCESS Update the file explorer form success. + @retval other errors Error occur when parse one directory. + +**/ +EFI_STATUS +LibUpdateFileExplorer ( + IN UINT16 KeyValue + ) +{ + UINT16 FileOptionMask; + MENU_ENTRY *NewMenuEntry; + FILE_CONTEXT *NewFileContext; + EFI_STATUS Status; + EFI_FILE_HANDLE FileHandle; + + Status = EFI_SUCCESS; + FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue) - mQuestionIdUpdate; + NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask); + NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext; + + if (NewFileContext->IsDir) { + RemoveEntryList (&NewMenuEntry->Link); + LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); + LibGetFileHandleFromMenu (NewMenuEntry, &FileHandle); + Status = LibFindFiles (FileHandle, NewFileContext->FileName, NewFileContext->DeviceHandle); + if (!EFI_ERROR (Status)) { + LibUpdateFileExplorePage (); + } else { + LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); + } + LibDestroyMenuEntry (NewMenuEntry); + } + + return Status; +} + +/** + Get the device path info saved in the menu structure. + + @param KeyValue Key value to identify the type of data to expect. + +**/ +VOID +LibGetDevicePath ( + IN UINT16 KeyValue + ) +{ + UINT16 FileOptionMask; + MENU_ENTRY *NewMenuEntry; + FILE_CONTEXT *NewFileContext; + + FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue) - mQuestionIdUpdate; + + NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask); + + NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext; + + if (gFileExplorerPrivate.RetDevicePath != NULL) { + FreePool (gFileExplorerPrivate.RetDevicePath); + } + gFileExplorerPrivate.RetDevicePath = DuplicateDevicePath (NewFileContext->DevicePath); +} + +/** + Choose a file in the specified directory. + + If user input NULL for the RootDirectory, will choose file in the system. + + If user input *File != NULL, function will return the allocate device path + info for the choosed file, caller has to free the memory after use it. + + @param RootDirectory Pointer to the root directory. + @param FileType The file type need to choose. + @param ChooseHandler Function pointer to the extra task need to do + after choose one file. + @param File Return the device path for the last time chosed file. + + @retval EFI_SUCESS Choose file success. + @retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL + One of them must not NULL. + @retval Other errors Choose file failed. +**/ +EFI_STATUS +EFIAPI +ChooseFile ( + IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory, + IN CHAR16 *FileType, OPTIONAL + IN CHOOSE_HANDLER ChooseHandler, OPTIONAL + OUT EFI_DEVICE_PATH_PROTOCOL **File OPTIONAL + ) +{ + EFI_FILE_HANDLE FileHandle; + EFI_STATUS Status; + UINT16 *FileName; + EFI_HANDLE DeviceHandle; + + if ((ChooseHandler == NULL) && (File == NULL)) { + return EFI_INVALID_PARAMETER; + } + + mQuestionIdUpdate = 0; + FileName = NULL; + + gFileExplorerPrivate.RetDevicePath = NULL; + gFileExplorerPrivate.ChooseHandler = ChooseHandler; + if (FileType != NULL) { + gFileExplorerPrivate.FileType = AllocateCopyPool (StrSize (FileType), FileType); + ASSERT(gFileExplorerPrivate.FileType != NULL); + LibToLowerString(gFileExplorerPrivate.FileType); + } else { + gFileExplorerPrivate.FileType = NULL; + } + + if (RootDirectory == NULL) { + Status = LibFindFileSystem(); + } else { + Status = LibGetFileHandleFromDevicePath(RootDirectory, &FileHandle, &FileName, &DeviceHandle); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = LibFindFiles (FileHandle, FileName, DeviceHandle); + } + if (EFI_ERROR (Status)) { + goto Done; + } + + LibUpdateFileExplorePage(); + + gFileExplorerPrivate.FormBrowser2->SendForm ( + gFileExplorerPrivate.FormBrowser2, + &gFileExplorerPrivate.FeHiiHandle, + 1, + &FileExplorerGuid, + 0, + NULL, + NULL + ); + +Done: + if ((Status == EFI_SUCCESS) && (File != NULL)) { + *File = gFileExplorerPrivate.RetDevicePath; + } else if (gFileExplorerPrivate.RetDevicePath != NULL) { + FreePool (gFileExplorerPrivate.RetDevicePath); + } + + if (gFileExplorerPrivate.FileType != NULL) { + FreePool (gFileExplorerPrivate.FileType); + } + + LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); + + if (FileName != NULL) { + FreePool (FileName); + } + + return Status; +} + +/** + + Install Boot Manager Menu driver. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCEESS Install File explorer library success. + +**/ +EFI_STATUS +EFIAPI +FileExplorerLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + gHiiVendorDevicePath = (HII_VENDOR_DEVICE_PATH*) DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*)&FeHiiVendorDevicePath); + ASSERT (gHiiVendorDevicePath != NULL); + CopyGuid (&gHiiVendorDevicePath->VendorDevicePath.Guid, &gEfiCallerIdGuid); + + // + // Install Device Path Protocol and Config Access protocol to driver handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &gFileExplorerPrivate.FeDriverHandle, + &gEfiDevicePathProtocolGuid, + gHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &gFileExplorerPrivate.FeConfigAccess, + NULL + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Post our File Explorer VFR binary to the HII database. + // + gFileExplorerPrivate.FeHiiHandle = HiiAddPackages ( + &FileExplorerGuid, + gFileExplorerPrivate.FeDriverHandle, + FileExplorerVfrBin, + FileExplorerLibStrings, + NULL + ); + ASSERT (gFileExplorerPrivate.FeHiiHandle != NULL); + + // + // Locate Formbrowser2 protocol + // + Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &gFileExplorerPrivate.FormBrowser2); + ASSERT_EFI_ERROR (Status); + + InitializeListHead (&gFileExplorerPrivate.FsOptionMenu->Head); + + return EFI_SUCCESS; +} + +/** + Unloads the application and its installed protocol. + + @param[in] ImageHandle Handle that identifies the image to be unloaded. + @param[in] SystemTable The system table. + + @retval EFI_SUCCESS The image has been unloaded. +**/ +EFI_STATUS +EFIAPI +FileExplorerLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + ASSERT (gHiiVendorDevicePath != NULL); + + if (gFileExplorerPrivate.FeDriverHandle != NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + gFileExplorerPrivate.FeDriverHandle, + &gEfiDevicePathProtocolGuid, + gHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &gFileExplorerPrivate.FeConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + + HiiRemovePackages (gFileExplorerPrivate.FeHiiHandle); + } + + FreePool (gHiiVendorDevicePath); + + return EFI_SUCCESS; +} + diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorer.h refind-0.13.2/filesystems/FileExplorerLib/FileExplorer.h --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorer.h 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorer.h 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,242 @@ +/** @file + File explorer lib. + +Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
    +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#ifndef _FILE_EXPLORER_H_ +#define _FILE_EXPLORER_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FormGuid.h" + +#define FILE_EXPLORER_CALLBACK_DATA_SIGNATURE SIGNATURE_32 ('f', 'e', 'c', 'k') + + +#pragma pack(1) + +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +typedef struct { + EFI_HANDLE DeviceHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_FILE_HANDLE FileHandle; + UINT16 *FileName; + + BOOLEAN IsRoot; + BOOLEAN IsDir; +} FILE_CONTEXT; + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + UINT16 *DisplayString; + UINT16 *HelpString; + EFI_STRING_ID DisplayStringToken; + EFI_STRING_ID HelpStringToken; + VOID *VariableContext; +} MENU_ENTRY; + +typedef struct { + UINTN Signature; + LIST_ENTRY Head; + UINTN MenuNumber; + BOOLEAN Used; +} MENU_OPTION; + +typedef struct { + // + // Shared callback data. + // + UINTN Signature; + + // + // File explorer formset callback data. + // + EFI_HII_HANDLE FeHiiHandle; + EFI_HANDLE FeDriverHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL FeConfigAccess; + EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + MENU_OPTION *FsOptionMenu; + CHAR16 *FileType; + CHOOSE_HANDLER ChooseHandler; + EFI_DEVICE_PATH_PROTOCOL *RetDevicePath; + +} FILE_EXPLORER_CALLBACK_DATA; + +#define FILE_EXPLORER_PRIVATE_FROM_THIS(a) CR (a, FILE_EXPLORER_CALLBACK_DATA, FeConfigAccess, FILE_EXPLORER_CALLBACK_DATA_SIGNATURE) + +#pragma pack() + +extern UINT8 FileExplorerVfrBin[]; + +#define MENU_OPTION_SIGNATURE SIGNATURE_32 ('m', 'e', 'n', 'u') +#define MENU_ENTRY_SIGNATURE SIGNATURE_32 ('e', 'n', 't', 'r') + +/// +/// Define the maximum characters that will be accepted. +/// +#define MAX_CHAR 480 +#define FILE_OPTION_OFFSET 0x8000 +#define FILE_OPTION_MASK 0x7FFF +#define QUESTION_ID_UPDATE_STEP 200 +#define MAX_FILE_NAME_LEN 20 +#define MAX_FOLDER_NAME_LEN 20 +#define NEW_FILE_QUESTION_ID_BASE 0x5000; +#define NEW_FOLDER_QUESTION_ID_BASE 0x6000; + +/** + This function processes the results of changes in configuration. + When user select a interactive opcode, this callback will be triggered. + Based on the Question(QuestionId) that triggers the callback, the corresponding + actions is performed. It handles: + + 1) the addition of boot option. + 2) the addition of driver option. + 3) exit from file browser + 4) update of file content if a dir is selected. + 5) boot the file if a file is selected in "boot from file" + + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original exporting driver + so that it can identify the type of data to expect. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original exporting driver. + @param ActionRequest On return, points to the action requested by the callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the callback. + +**/ +EFI_STATUS +EFIAPI +LibCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ); + + +/** + This function allows a caller to extract the current configuration for one + or more named elements from the target driver. + + + @param This - Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Request - A null-terminated Unicode string in format. + @param Progress - On return, points to a character in the Request string. + Points to the string's null terminator if request was successful. + Points to the most recent '&' before the first failing name/value + pair (or the beginning of the string if the failure is in the + first name/value pair) if the request was not successful. + @param Results - A null-terminated Unicode string in format which + has all values filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results is filled with the requested values. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. + @retval EFI_INVALID_PARAMETER Request is NULL, illegal syntax, or unknown name. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +LibExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ); + +/** + This function processes the results of changes in configuration. + + + @param This - Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Configuration - A null-terminated Unicode string in format. + @param Progress - A pointer to a string filled in with the offset of the most + recent '&' before the first failing name/value pair (or the + beginning of the string if the failure is in the first + name/value pair) or the terminating NULL if all was successful. + + @retval EFI_SUCCESS The Results is processed successfully. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. + +**/ +EFI_STATUS +EFIAPI +LibRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ); + +/** + Update the file explower page with the refershed file system. + + @param KeyValue Key value to identify the type of data to expect. + + @retval EFI_SUCCESS Update the file explorer form success. + @retval other errors Error occur when parse one directory. + +**/ +EFI_STATUS +LibUpdateFileExplorer ( + IN UINT16 KeyValue + ); + + +/** + Get the device path info saved in the menu structure. + + @param KeyValue Key value to identify the type of data to expect. + +**/ +VOID +LibGetDevicePath ( + IN UINT16 KeyValue + ); + +#endif diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorerLib.inf refind-0.13.2/filesystems/FileExplorerLib/FileExplorerLib.inf --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorerLib.inf 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorerLib.inf 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,63 @@ +## @file +# library defines a set of interfaces for how to do file explorer. +# +# Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.
    +# This program and the accompanying materials are licensed and made available under +# the terms and conditions of the BSD License that accompanies this distribution. +# The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FileExplorerLib + MODULE_UNI_FILE = FileExplorerLib.uni + FILE_GUID = 4FC9C630-0F90-4053-8F13-264CBD22FC58 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = FileExplorerLib|DXE_DRIVER UEFI_APPLICATION + CONSTRUCTOR = FileExplorerLibConstructor + DESTRUCTOR = FileExplorerLibDestructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + FileExplorer.h + FileExplorerVfr.vfr + FileExplorerString.uni + FileExplorer.c + FormGuid.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DevicePathLib + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + HiiLib + UefiHiiServicesLib + +[Guids] + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## GUID (Indicate the information type is volume) + gEfiIfrTianoGuid ## SOMETIMES_CONSUMES ## GUID (Extended IFR Guid Opcode) + +[Protocols] + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiFormBrowser2ProtocolGuid ## CONSUMES + gEfiDevicePathToTextProtocolGuid ## PRODUCES + +[Depex.common.DXE_DRIVER] + gEfiFormBrowser2ProtocolGuid AND gEfiHiiDatabaseProtocolGuid \ No newline at end of file diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorerLib.uni refind-0.13.2/filesystems/FileExplorerLib/FileExplorerLib.uni --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorerLib.uni 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorerLib.uni 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,26 @@ +// /** @file +// library defines a set of interfaces for how to do file explorer. +// +// library defines a set of interfaces for how to do file explorer. +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
    +// +// This program and the accompanying materials are licensed and made available under +// the terms and conditions of the BSD License that accompanies this distribution. +// The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php. +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_MODULE_ABSTRACT +#language en-US +"library defines a set of interfaces for how to do file explorer." + +#string STR_MODULE_DESCRIPTION +#language en-US +"library defines a set of interfaces for how to do file explorer." + + diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorerString.uni refind-0.13.2/filesystems/FileExplorerLib/FileExplorerString.uni --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorerString.uni 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorerString.uni 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,61 @@ +///** @file +// +// Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.
    +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// Module Name: +// +// FileExplorerString.uni +// +// Abstract: +// +// String definitions for file exporer library. +// +// Revision History: +// +// --*/ +/=# + +#langdef en-US "English" +#langdef fr-FR "Français" + +#string STR_NULL_STRING #language en-US " " + #language fr-FR " " +#string STR_FILE_EXPLORER_TITLE #language en-US "File Explorer" + #language fr-FR "File Explorer" +#string STR_NEW_FILE #language en-US "***NEW FILE***" + #language fr-FR "***NEW FILE***" +#string STR_NEW_FILE_HELP #language en-US "This menu used to create a new file in current directory, jump to next page to name the new file" + #language fr-FR "This menu used to create a new file in current directory, jump to next page to name the new file" +#string STR_ADD_NEW_FILE_TITLE #language en-US "Create a new file" + #language fr-FR "Create a new file" +#string STR_ADD_NEW_FOLDER_TITLE #language en-US "Create a new folder" + #language fr-FR "Create a new folder" +#string STR_NEW_FILE_NAME_PROMPT #language en-US "File Name" + #language fr-FR "File Name" +#string STR_NEW_FILE_NAME_HELP #language en-US "Please input a name for the new file" + #language fr-FR "Please input a name for the new file" +#string STR_CREATE_FILE_AND_EXIT #language en-US "Create File and Exit" + #language fr-FR "Create File and Exit" +#string STR_NO_CREATE_FILE_AND_EXIT #language en-US "Discard Create and Exit" + #language fr-FR "Discard Create and Exit" +#string STR_NEW_FOLDER #language en-US "***NEW FOLDER***" + #language fr-FR "***NEW FOLDER***" +#string STR_NEW_FOLDER_HELP #language en-US "This menu used to create a new folder in current directory, jump to next page to name the new folder" + #language fr-FR "This menu used to create a new folder in current directory, jump to next page to name the new folder" +#string STR_ADD_NEW_FOLDER_TITLE #language en-US "Create a new folder" + #language fr-FR "Create a new folder" +#string STR_NEW_FOLDER_NAME_PROMPT #language en-US "Folder Name" + #language fr-FR "Folder Name" +#string STR_NEW_FOLDER_NAME_HELP #language en-US "Please input a name for the new folder" + #language fr-FR "Please input a name for the new folder" +#string STR_CREATE_FOLDER_AND_EXIT #language en-US "Create Folder and Exit" + #language fr-FR "Create Folder and Exit" +#string STR_NO_CREATE_FOLDER_AND_EXIT #language en-US "Discard Create and Exit" + #language fr-FR "Discard Create and Exit" diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FileExplorerVfr.vfr refind-0.13.2/filesystems/FileExplorerLib/FileExplorerVfr.vfr --- refind-0.12.0/filesystems/FileExplorerLib/FileExplorerVfr.vfr 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FileExplorerVfr.vfr 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,85 @@ +///** @file +// +// File Explorer Formset +// +// Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.
    +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +//**/ + +#include "FormGuid.h" + +formset + guid = EFI_FILE_EXPLORE_FORMSET_GUID, + title = STRING_TOKEN(STR_FILE_EXPLORER_TITLE), + help = STRING_TOKEN(STR_NULL_STRING), + classguid = EFI_FILE_EXPLORE_FORMSET_GUID, + + form formid = FORM_FILE_EXPLORER_ID, + title = STRING_TOKEN(STR_FILE_EXPLORER_TITLE); + + label FORM_FILE_EXPLORER_ID; + label LABEL_END; + endform; + + form formid = FORM_ADD_NEW_FILE_ID, + title = STRING_TOKEN(STR_ADD_NEW_FILE_TITLE); + + string + prompt = STRING_TOKEN(STR_NEW_FILE_NAME_PROMPT), + help = STRING_TOKEN(STR_NEW_FILE_NAME_HELP), + flags = INTERACTIVE, + key = NEW_FILE_NAME_ID, + minsize = 2, + maxsize = 20, + endstring; + + subtitle text = STRING_TOKEN(STR_NULL_STRING); + + text + help = STRING_TOKEN(STR_CREATE_FILE_AND_EXIT), + text = STRING_TOKEN(STR_CREATE_FILE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_VALUE_CREATE_FILE_AND_EXIT; + + text + help = STRING_TOKEN(STR_NO_CREATE_FILE_AND_EXIT), + text = STRING_TOKEN(STR_NO_CREATE_FILE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_VALUE_NO_CREATE_FILE_AND_EXIT; + endform; + + form formid = FORM_ADD_NEW_FOLDER_ID, + title = STRING_TOKEN(STR_ADD_NEW_FOLDER_TITLE); + + string + prompt = STRING_TOKEN(STR_NEW_FOLDER_NAME_PROMPT), + help = STRING_TOKEN(STR_NEW_FOLDER_NAME_HELP), + flags = INTERACTIVE, + key = NEW_FOLDER_NAME_ID, + minsize = 2, + maxsize = 20, + endstring; + + subtitle text = STRING_TOKEN(STR_NULL_STRING); + + text + help = STRING_TOKEN(STR_CREATE_FOLDER_AND_EXIT), + text = STRING_TOKEN(STR_CREATE_FOLDER_AND_EXIT), + flags = INTERACTIVE, + key = KEY_VALUE_CREATE_FOLDER_AND_EXIT; + + text + help = STRING_TOKEN(STR_NO_CREATE_FOLDER_AND_EXIT), + text = STRING_TOKEN(STR_NO_CREATE_FOLDER_AND_EXIT), + flags = INTERACTIVE, + key = KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT; + endform; + +endformset; \ No newline at end of file diff -Nru refind-0.12.0/filesystems/FileExplorerLib/FormGuid.h refind-0.13.2/filesystems/FileExplorerLib/FormGuid.h --- refind-0.12.0/filesystems/FileExplorerLib/FormGuid.h 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/filesystems/FileExplorerLib/FormGuid.h 2021-02-21 21:40:57.000000000 +0000 @@ -0,0 +1,38 @@ +/** @file +Formset guids, form id and VarStore data structure for File explorer library. + +Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef _FILE_EXPLORER_FORM_GUID_H_ +#define _FILE_EXPLORER_FORM_GUID_H_ + + +#define EFI_FILE_EXPLORE_FORMSET_GUID \ + { \ + 0xfe561596, 0xe6bf, 0x41a6, {0x83, 0x76, 0xc7, 0x2b, 0x71, 0x98, 0x74, 0xd0} \ + } + +#define FORM_FILE_EXPLORER_ID 0x1000 +#define FORM_ADD_NEW_FILE_ID 0x2000 +#define NEW_FILE_NAME_ID 0x2001 +#define KEY_VALUE_CREATE_FILE_AND_EXIT 0x2002 +#define KEY_VALUE_NO_CREATE_FILE_AND_EXIT 0x2003 +#define FORM_ADD_NEW_FOLDER_ID 0x3000 +#define NEW_FOLDER_NAME_ID 0x3001 +#define KEY_VALUE_CREATE_FOLDER_AND_EXIT 0x3002 +#define KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT 0x3003 + +#define LABEL_END 0xffff + +#endif + diff -Nru refind-0.12.0/filesystems/fsw_btrfs.c refind-0.13.2/filesystems/fsw_btrfs.c --- refind-0.12.0/filesystems/fsw_btrfs.c 2018-10-22 23:19:10.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_btrfs.c 2021-02-21 21:40:57.000000000 +0000 @@ -726,6 +726,7 @@ struct fsw_btrfs_volume *vol = (struct fsw_btrfs_volume *)volg; struct btrfs_superblock sb; fsw_status_t err; + btrfs_uuid_t u; if(vol->n_devices_attached >= vol->n_devices_allocated) return FSW_UNSUPPORTED; @@ -734,7 +735,11 @@ if(err) return FSW_UNSUPPORTED; - if(!uuid_eq(vol->uuid, sb.uuid)) + u[0] = sb.uuid[0]; + u[1] = sb.uuid[1]; + u[2] = sb.uuid[2]; + u[3] = sb.uuid[3]; + if(!uuid_eq(vol->uuid, u)) return FSW_UNSUPPORTED; return btrfs_add_multi_device(vol, slave, &sb); diff -Nru refind-0.12.0/filesystems/fsw_efi.c refind-0.13.2/filesystems/fsw_efi.c --- refind-0.12.0/filesystems/fsw_efi.c 2020-03-13 12:41:59.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_efi.c 2021-03-13 23:58:28.000000000 +0000 @@ -76,7 +76,7 @@ /** Helper macro for stringification. */ #define FSW_EFI_STRINGIFY(x) #x /** Expands to the EFI driver name given the file system type name. */ -#define FSW_EFI_DRIVER_NAME(t) L"rEFInd 0.12.0 " FSW_EFI_STRINGIFY(t) L" File System Driver" +#define FSW_EFI_DRIVER_NAME(t) L"rEFInd 0.13.2 " FSW_EFI_STRINGIFY(t) L" File System Driver" // function prototypes diff -Nru refind-0.12.0/filesystems/fsw_ext2.c refind-0.13.2/filesystems/fsw_ext2.c --- refind-0.12.0/filesystems/fsw_ext2.c 2017-05-26 13:19:44.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_ext2.c 2021-03-13 23:21:51.000000000 +0000 @@ -536,7 +536,7 @@ int ea_blocks; struct fsw_string s; - if (dno->g.size > FSW_PATH_MAX) + if ((dno->g.size > FSW_PATH_MAX) || (dno->g.size > sizeof(dno->raw->i_block))) return FSW_VOLUME_CORRUPTED; ea_blocks = dno->raw->i_file_acl ? (vol->g.log_blocksize >> 9) : 0; diff -Nru refind-0.12.0/filesystems/fsw_ext4.c refind-0.13.2/filesystems/fsw_ext4.c --- refind-0.12.0/filesystems/fsw_ext4.c 2017-05-26 13:19:44.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_ext4.c 2021-03-13 23:19:35.000000000 +0000 @@ -141,7 +141,8 @@ if (vol->sb->s_rev_level == EXT4_DYNAMIC_REV && (vol->sb->s_feature_incompat & ~(EXT4_FEATURE_INCOMPAT_FILETYPE | EXT4_FEATURE_INCOMPAT_RECOVER | EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FLEX_BG | - EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_META_BG))) + EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_META_BG | + EXT4_FEATURE_INCOMPAT_ENCRYPT))) return FSW_UNSUPPORTED; if (vol->sb->s_rev_level == EXT4_DYNAMIC_REV && @@ -180,13 +181,13 @@ } // Calculate group descriptor count the way the kernel does it... - groupcnt = (vol->sb->s_blocks_count_lo - vol->sb->s_first_data_block + + groupcnt = (vol->sb->s_blocks_count_lo - vol->sb->s_first_data_block + vol->sb->s_blocks_per_group - 1) / vol->sb->s_blocks_per_group; - // Descriptors in one block... s_desc_size needs to be set! (Usually 128 since normal block + // Descriptors in one block... s_desc_size needs to be set! (Usually 128 since normal block // descriptors are 32 byte and block size is 4096) gdesc_per_block = EXT4_DESC_PER_BLOCK(vol->sb); - + // Read the group descriptors to get inode table offsets status = fsw_alloc(sizeof(fsw_u64) * groupcnt, &vol->inotab_bno); if (status) @@ -201,7 +202,7 @@ // If option meta_bg is set, the block group descriptor is in meta block group... metabg_of_gdesc = (fsw_u32)(groupno / gdesc_per_block) * gdesc_per_block; gdesc_bno = fsw_ext4_group_first_block_no(vol->sb, metabg_of_gdesc); - // We need to know if the block group in questition has a super block, if yes, the + // We need to know if the block group in questition has a super block, if yes, the // block group descriptors are in the next block number if(!(vol->sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER) || fsw_ext4_group_sparse(metabg_of_gdesc)) gdesc_bno += 1; @@ -415,7 +416,7 @@ while(1) { ext4_extent_header = (struct ext4_extent_header *)((char *)buffer + buf_offset); buf_offset += sizeof(struct ext4_extent_header); - FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"), + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"), ext4_extent_header->eh_entries)); if(ext4_extent_header->eh_magic != EXT4_EXT_MAGIC) return FSW_VOLUME_CORRUPTED; @@ -440,7 +441,7 @@ } else { - FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"), + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"), ext4_extent_header->eh_depth)); ext4_extent_idx = (struct ext4_extent_idx *)((char *)buffer + buf_offset); buf_offset += sizeof(struct ext4_extent_idx); @@ -722,7 +723,7 @@ int ea_blocks; struct fsw_string s; - if (dno->g.size > FSW_PATH_MAX) + if ((dno->g.size > FSW_PATH_MAX) || (dno->g.size > sizeof(dno->raw->i_block))) return FSW_VOLUME_CORRUPTED; /* Linux kernels ext4_inode_is_fast_symlink... */ diff -Nru refind-0.12.0/filesystems/fsw_ext4_disk.h refind-0.13.2/filesystems/fsw_ext4_disk.h --- refind-0.12.0/filesystems/fsw_ext4_disk.h 2015-11-29 18:54:48.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_ext4_disk.h 2021-02-21 21:40:57.000000000 +0000 @@ -400,6 +400,7 @@ #define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */ #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ #define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */ +#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 /* BK ext4 fscrypt encryption */ #define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ EXT4_FEATURE_INCOMPAT_RECOVER| \ diff -Nru refind-0.12.0/filesystems/fsw_hfs.c refind-0.13.2/filesystems/fsw_hfs.c --- refind-0.12.0/filesystems/fsw_hfs.c 2015-11-29 18:54:48.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_hfs.c 2021-03-13 23:28:29.000000000 +0000 @@ -328,8 +328,9 @@ /* set default/fallback volume name */ s.type = FSW_STRING_TYPE_ISO88591; - s.size = s.len = kHFSMaxVolumeNameChars; - s.data = "HFS+ volume"; + #define kHFSVolumeNameFallback "HFS+ volume" + s.size = s.len = (sizeof(kHFSVolumeNameFallback) - 1); + s.data = kHFSVolumeNameFallback; status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); CHECK(status); diff -Nru refind-0.12.0/filesystems/fsw_hfs.h refind-0.13.2/filesystems/fsw_hfs.h --- refind-0.12.0/filesystems/fsw_hfs.h 2015-11-29 18:54:48.000000000 +0000 +++ refind-0.13.2/filesystems/fsw_hfs.h 2021-02-21 21:40:57.000000000 +0000 @@ -74,8 +74,8 @@ struct HFSPlusExtentKey ext_key; struct HFSPlusCatalogKey cat_key; fsw_u16 key_len; /* Length is at the beginning of all keys */ - }; -}; + } HFS_ALIGNMENT; +} HFS_ALIGNMENT; #pragma pack() diff -Nru refind-0.12.0/filesystems/RamDiskDxe/RamDiskDxe.inf refind-0.13.2/filesystems/RamDiskDxe/RamDiskDxe.inf --- refind-0.12.0/filesystems/RamDiskDxe/RamDiskDxe.inf 2017-05-29 16:44:33.000000000 +0000 +++ refind-0.13.2/filesystems/RamDiskDxe/RamDiskDxe.inf 2021-02-21 21:40:57.000000000 +0000 @@ -55,7 +55,7 @@ UefiHiiServicesLib MemoryAllocationLib HiiLib - FileExplorerLib +# FileExplorerLib DevicePathLib PrintLib PcdLib diff -Nru refind-0.12.0/filesystems/RamDiskDxe/RamDiskImpl.c refind-0.13.2/filesystems/RamDiskDxe/RamDiskImpl.c --- refind-0.12.0/filesystems/RamDiskDxe/RamDiskImpl.c 2017-05-29 16:44:33.000000000 +0000 +++ refind-0.13.2/filesystems/RamDiskDxe/RamDiskImpl.c 2021-02-21 21:40:57.000000000 +0000 @@ -645,7 +645,8 @@ if (Action == EFI_BROWSER_ACTION_CHANGING) { switch (QuestionId) { case MAIN_GOTO_FILE_EXPLORER_ID: - Status = ChooseFile (NULL, NULL, NULL, &FileDevPath); + Status = EFI_SUCCESS; +// Status = ChooseFile (NULL, NULL, NULL, &FileDevPath); if (EFI_ERROR (Status)) { break; } diff -Nru refind-0.12.0/fonts/mkfont.sh refind-0.13.2/fonts/mkfont.sh --- refind-0.12.0/fonts/mkfont.sh 2018-07-22 20:55:52.000000000 +0000 +++ refind-0.13.2/fonts/mkfont.sh 2021-02-21 21:40:57.000000000 +0000 @@ -36,7 +36,7 @@ exit 1 fi -Convert=`which convert 2> /dev/null` +Convert="$(command -v convert 2> /dev/null)" if [[ ! -x $Convert ]] ; then echo "The 'convert' program is required but could not be found. It's part of the" echo "ImagMagick program, usually installed in the 'imagemagick' package." Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/icons/os_manjaro.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/icons/os_manjaro.png differ Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/icons/os_uefi.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/icons/os_uefi.png differ diff -Nru refind-0.12.0/icons/README refind-0.13.2/icons/README --- refind-0.12.0/icons/README 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/icons/README 2021-02-28 23:16:44.000000000 +0000 @@ -30,6 +30,10 @@ - Copyright (c) 2008 Dan Rabbit - License: GPLv2+ +- Manjaro OS icon + - Source: https://commons.wikimedia.org/wiki/File:Manjaro-logo.svg + - Public domain + - Ubuntu "animal" icons (through os_xenial.png) - Source: https://wiki.ubuntu.com/Artwork/Official - Copyright (c) 2014-2017 Canonical Ltd. @@ -68,6 +72,7 @@ os_mandriva.png -- AwOken/clear/128x128/start-here/start-here-mandriva5.png os_network.png -- AwOken/clear/128x128/places/network-workgroup1.png os_ubuntu.png -- AwOken/clear/128x128/start-here/start-here-ubuntu.png +os_uefi.png -- AwOkenWhite/clear/128x128/start-here/start-here-umbrella3.png os_unknown.png -- AwOken/clear/128x128/actions/color-line1.png os_win8.png -- AwOken/clear/128x128/apps/live1.png @@ -97,6 +102,7 @@ tool_mok_tool.png -- AwOkenWhite/clear/128x128/apps/gnome-keyring-manager.png tool_netboot.png -- AwOken/clear/128x128/places/network-workgroup1.png tool_shell.png -- AwOken/clear/128x128/apps/terminal3.png +vol_efi.png -- AwOkenWhite/clear/128x128/status/indicator-cpufreq.png vol_external.png -- AwOkenWhite/clear/128x128/devices/drive-removable-media-usb2.png vol_internal.png -- AwOken/clear/128/128/drive-harddisk/Internal.png vol_net.png -- AwOken/clear/128/128/drive-harddisk/Server.png Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/icons/vol_efi.png and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/icons/vol_efi.png differ diff -Nru refind-0.12.0/include/version.h refind-0.13.2/include/version.h --- refind-0.12.0/include/version.h 2020-03-13 12:41:07.000000000 +0000 +++ refind-0.13.2/include/version.h 2021-03-13 23:58:09.000000000 +0000 @@ -2,7 +2,7 @@ * include/version.h * Version number header file * - * Copyright (c) 2017-2020 Roderick W. Smith + * Copyright (c) 2017-2021 Roderick W. Smith * * Distributed under the terms of the GNU General Public License (GPL) * version 3 (GPLv3), a copy of which must be distributed with this @@ -10,5 +10,5 @@ * */ -#define REFIND_VERSION L"0.12.0" +#define REFIND_VERSION L"0.13.2" Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/keys/centos.cer and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/keys/centos.cer differ diff -Nru refind-0.12.0/keys/centos.crt refind-0.13.2/keys/centos.crt --- refind-0.12.0/keys/centos.crt 2015-12-13 04:58:55.000000000 +0000 +++ refind-0.13.2/keys/centos.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2DCCBMCgAwIBAgIQAth8/dxLyW/uz8zPJTA1WTANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBFViBDb2RlIFNpZ25p -bmcgQ0EgKFNIQTIpMB4XDTE0MDYwMzAwMDAwMFoXDTE3MDYwNzEyMDAwMFowgfcx -HTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMT -AlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQFEwcyOTQ1NDM2 -MR4wHAYDVQQJExUxMDAgRWFzdCBEYXZpZSBTdHJlZXQxDjAMBgNVBBETBTI3NjAx -MQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcT -B1JhbGVpZ2gxFTATBgNVBAoTDFJlZCBIYXQgSW5jLjEVMBMGA1UEAxMMUmVkIEhh -dCBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyJOAuVmPBea5 -KxWReGdj02KIv9+qojLW6EPA4bCFuRQ8hRwbdLZoZSoDg6rCh7Wg4naSX5FC6uaA -FdJ0j7Rqrj/eO5mJzW5KpfsaPAMysFKAfE5oZ3jKMQSlkP+8JK2AmBTj0nIyS6NP -mL5tFuD/EExGp043wnWSQOA6EWoZWwS5AOGW5+biojFWwYjq2kVxp9uec2HwSJ8U -fH6DSdKZdEeGTKXMhFdphvCFaseRZkJTsKV6Y1LdcaHn/PkL+Wlz6IOYSqF5OpiZ -D0ESH5d7kWxRm/XerZMjZ2Y7oNuaMxl8bfMIIlqErD4DyMkZ6lCCz5fAt8mIxsm3 -TDYUei9viQIDAQABo4IB6DCCAeQwHwYDVR0jBBgwFoAUj+h+8G0yagAFI8dwl2o6 -kP9r6tQwHQYDVR0OBBYEFB/5bdjRsjJyKMBLA6dy27Lbt5sfMC4GA1UdEQQnMCWg -IwYIKwYBBQUHCAOgFzAVDBNVUy1ERUxBV0FSRS0yOTQ1NDM2MA4GA1UdDwEB/wQE -AwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzB7BgNVHR8EdDByMDegNaAzhjFodHRw -Oi8vY3JsMy5kaWdpY2VydC5jb20vRVZDb2RlU2lnbmluZ1NIQTItZzEuY3JsMDeg -NaAzhjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRVZDb2RlU2lnbmluZ1NIQTIt -ZzEuY3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb9bAMCMCowKAYIKwYBBQUHAgEWHGh0 -dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwfgYIKwYBBQUHAQEEcjBwMCQGCCsG -AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSAYIKwYBBQUHMAKGPGh0 -dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEVWQ29kZVNpZ25pbmdD -QS1TSEEyLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBynOz2 -SzOBjev7IxtrKQiCx2lv8LoG5MILQJTuepyyoMcbVDJj2arA0W8ZQfClRjUt9qlL -fbCbAJyk57NJZRGyTf6oxp/1J5Nz4PFYFPqPodHBXgfi2JefBA6WAdYKGKsE/qtA -mYPsv5LAg3AmSkTCLdGuE155WFzD1GhwKEEC44l8QiQ2xHoGJO/EWobBQUvpmxzh -1wu3OcgD/TGJoE+jqm1qKvMqDKLh8BlhuX+RP0f40AGcd+Yf22NkhM0o+3ck9OjS -nBVMA8WlbFLvz7DB+r58x4gSxp7cvo5Fvq1mV2g+cUSR7zwNALQBT0U3cMidQlAD -ibMnawpjKcS7WsDO ------END CERTIFICATE----- Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/keys/centossecureboot201.cer and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/keys/centossecureboot201.cer differ diff -Nru refind-0.12.0/keys/centossecureboot201.crt refind-0.13.2/keys/centossecureboot201.crt --- refind-0.12.0/keys/centossecureboot201.crt 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/keys/centossecureboot201.crt 2021-03-07 18:26:35.000000000 +0000 @@ -0,0 +1,84 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 93:c2:04:d8:bd:77:6b:11 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=CentOS Secure Boot CA 2/emailAddress=security@centos.org + Validity + Not Before: Jun 9 10:04:20 2020 GMT + Not After : Jan 18 10:04:20 2038 GMT + Subject: CN=CentOS Secure Boot Signing 201/emailAddress=security@centos.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9e:ef:fe:76:1c:9f:9b:3e:f2:e4:c5:29:bd:19: + 32:01:59:f3:e6:99:fa:eb:b5:f8:94:0c:95:3a:65: + 5e:b1:72:d0:50:3e:70:64:8a:1a:d1:f6:4d:af:6d: + 57:ee:40:71:40:09:dd:30:0c:81:a1:8b:26:63:12: + 07:bf:e1:d1:45:9f:9b:09:a6:57:98:9e:ef:97:e9: + bd:68:38:ea:aa:63:92:2e:0d:2f:8e:fb:be:88:40: + 9b:59:e3:bc:b7:6f:e3:bb:6b:1e:6e:9e:ee:57:b8: + 28:c6:d5:d6:bf:47:a6:e9:38:a9:8f:08:73:98:49: + a8:58:d2:62:73:f1:1e:44:d4:88:3d:f9:aa:43:e2: + 72:2e:d7:43:3e:1d:b6:65:f6:d1:2e:ef:31:cb:9f: + 5e:e3:d4:ea:3c:23:9a:07:af:f9:4a:ee:43:9a:75: + 06:ed:9a:54:2c:ed:5b:ca:85:a5:10:16:cd:30:64: + ea:d5:27:7e:23:f6:fc:ec:69:a9:43:2f:78:73:6b: + 33:78:8b:f8:54:db:3f:ce:95:a4:5a:04:9a:15:49: + 98:cd:34:7c:c7:8c:a9:8a:32:82:ae:c0:d6:34:93: + e7:d2:54:82:45:ee:eb:54:9a:96:d4:da:4b:24:f8: + 09:56:d8:cd:7f:ec:7b:f3:bd:db:9b:8c:b6:18:87: + fa:07 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: critical + Code Signing + X509v3 Subject Key Identifier: + 5D:4B:64:F2:FA:63:1E:5E:5F:DB:AA:DC:14:67:C6:6C:99:21:7A:22 + X509v3 Authority Key Identifier: + keyid:70:00:7F:99:20:9C:12:6B:E1:47:74:EA:EC:7B:6D:96:31:F3:4D:CA + + Signature Algorithm: sha256WithRSAEncryption + 39:4b:b5:cc:37:3f:cd:db:84:0f:63:7c:c4:e4:53:fb:5e:fd: + db:12:19:23:6f:0a:50:14:fd:4f:7c:f9:87:3d:f9:6d:5b:af: + 07:a5:94:34:1b:84:07:f4:f1:a0:de:cc:73:87:99:31:c3:93: + 66:c0:bc:f2:0f:b2:69:65:8e:da:b9:1a:8e:ae:38:56:f3:7c: + 5a:8d:29:0d:3d:ad:84:e7:86:31:a2:8e:2a:a8:f8:f8:f7:87: + 32:65:5d:81:47:53:b8:40:c5:1b:a7:46:1f:b0:60:a7:b4:97: + 89:51:26:3c:de:46:b9:14:d5:a0:7d:99:cc:a7:7e:ed:89:18: + 02:ce:e6:07:45:49:e2:04:7d:5b:03:65:ec:e6:c3:86:0d:82: + 31:24:45:51:ec:15:ad:31:83:a8:1c:6e:52:4d:b8:0f:5d:0b: + e4:7b:51:49:39:46:8a:0b:fd:0c:46:af:b4:19:65:0f:12:f1: + fc:ee:fd:6b:4f:df:9a:73:7c:e0:c8:3d:c3:d5:b5:ab:4a:86: + 36:97:e8:89:fb:af:f4:f1:c2:05:5d:17:fb:b6:df:a5:0e:45: + 89:db:89:99:93:ce:f0:4e:e9:9c:f4:4a:03:b0:6e:be:a2:69: + ab:b1:f3:3b:ed:c7:97:f4:0e:0a:53:27:5a:7e:70:9a:35:ea: + 7a:76:d1:bc +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIJAJPCBNi9d2sRMA0GCSqGSIb3DQEBCwUAMEYxIDAeBgNV +BAMMF0NlbnRPUyBTZWN1cmUgQm9vdCBDQSAyMSIwIAYJKoZIhvcNAQkBFhNzZWN1 +cml0eUBjZW50b3Mub3JnMB4XDTIwMDYwOTEwMDQyMFoXDTM4MDExODEwMDQyMFow +TTEnMCUGA1UEAwweQ2VudE9TIFNlY3VyZSBCb290IFNpZ25pbmcgMjAxMSIwIAYJ +KoZIhvcNAQkBFhNzZWN1cml0eUBjZW50b3Mub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAnu/+dhyfmz7y5MUpvRkyAVnz5pn667X4lAyVOmVesXLQ +UD5wZIoa0fZNr21X7kBxQAndMAyBoYsmYxIHv+HRRZ+bCaZXmJ7vl+m9aDjqqmOS +Lg0vjvu+iECbWeO8t2/ju2sebp7uV7goxtXWv0em6TipjwhzmEmoWNJic/EeRNSI +PfmqQ+JyLtdDPh22ZfbRLu8xy59e49TqPCOaB6/5Su5DmnUG7ZpULO1byoWlEBbN +MGTq1Sd+I/b87GmpQy94c2szeIv4VNs/zpWkWgSaFUmYzTR8x4ypijKCrsDWNJPn +0lSCRe7rVJqW1NpLJPgJVtjNf+x7873bm4y2GIf6BwIDAQABo3gwdjAMBgNVHRMB +Af8EAjAAMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAd +BgNVHQ4EFgQUXUtk8vpjHl5f26rcFGfGbJkheiIwHwYDVR0jBBgwFoAUcAB/mSCc +EmvhR3Tq7HttljHzTcowDQYJKoZIhvcNAQELBQADggEBADlLtcw3P83bhA9jfMTk +U/te/dsSGSNvClAU/U98+Yc9+W1brwellDQbhAf08aDezHOHmTHDk2bAvPIPsmll +jtq5Go6uOFbzfFqNKQ09rYTnhjGijiqo+Pj3hzJlXYFHU7hAxRunRh+wYKe0l4lR +JjzeRrkU1aB9mcynfu2JGALO5gdFSeIEfVsDZezmw4YNgjEkRVHsFa0xg6gcblJN +uA9dC+R7UUk5RooL/QxGr7QZZQ8S8fzu/WtP35pzfODIPcPVtatKhjaX6In7r/Tx +wgVdF/u236UORYnbiZmTzvBO6Zz0SgOwbr6iaaux8zvtx5f0DgpTJ1p+cJo16np2 +0bw= +-----END CERTIFICATE----- Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/keys/centossecurebootca2.cer and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/keys/centossecurebootca2.cer differ diff -Nru refind-0.12.0/keys/centossecurebootca2.crt refind-0.13.2/keys/centossecurebootca2.crt --- refind-0.12.0/keys/centossecurebootca2.crt 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/keys/centossecurebootca2.crt 2021-03-07 18:26:35.000000000 +0000 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYjCCAkqgAwIBAgIJAIlReu6IOzL7MA0GCSqGSIb3DQEBCwUAMEYxIDAeBgNV +BAMMF0NlbnRPUyBTZWN1cmUgQm9vdCBDQSAyMSIwIAYJKoZIhvcNAQkBFhNzZWN1 +cml0eUBjZW50b3Mub3JnMB4XDTIwMDYwOTA4MTkzMloXDTM4MDExODA4MTkzMlow +RjEgMB4GA1UEAwwXQ2VudE9TIFNlY3VyZSBCb290IENBIDIxIjAgBgkqhkiG9w0B +CQEWE3NlY3VyaXR5QGNlbnRvcy5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQChatbNaQDV0RTCqff1tl92xI6gu1k8jYufW8FyzZ6uDnxoGpBT0LiU +WKuGjMQ89JgiApFzDYSLWrZg8NbTnVdz0hny4SMyspe5weUk6IToKXvEejZNFn6i +vae2vfT0/ASKsgIvUcz4sWHMK43vbfv/pVpYGLgoG5aNUkt7VhkeURwJzR3ODgDp +aL4bQ/7qEo8ASHCEvQx6klG330Z06O0kjS6GK12cPC1t5ZlimVXCNWP1jf0pMWmh +aBrZjbyY0j8R7Yns3cEovAM230chsVdyFxSYpqCLzMlmWNxiIlvcAoDIRMWEa7Da +SSAfJWH+ygAzad1PHlnCB0zAFbLAMJH1AgMBAAGjUzBRMB0GA1UdDgQWBBRwAH+Z +IJwSa+FHdOrse22WMfNNyjAfBgNVHSMEGDAWgBRwAH+ZIJwSa+FHdOrse22WMfNN +yjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAe5NcVSUd/POZs +Jkiep8ATNwXglLAeYxB55F42sXx5OOdKMBmhqWQIVJvaih/wsfKIBfdUGv2L9dH8 +IQgiU1PRYx0baSVJno3HcQTbCqLvnvckusR7IUTDAFj774MvXwS6yV6pXzxDmuh2 +t8hRktOKFeUtdlDYqg9X3Ia3GkoB5huyEbuaZTNcV4TAfU/yAERNIAgRs+fLQU70 +OgGlWsp35J8qPkZKabGf0surDa2xa6iAoFyknxruoKQ8uNSB9KB7/0JvVouNx90+ +ncykWW96GVKs8+H5WGza10FqrchtThSNCSXTtLbTXoK0Atdvu0o04XUbsCGMnlcG +zAVb3/m0 +-----END CERTIFICATE----- Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/keys/debian.cer and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/keys/debian.cer differ diff -Nru refind-0.12.0/keys/README.txt refind-0.13.2/keys/README.txt --- refind-0.12.0/keys/README.txt 2020-02-13 03:28:13.000000000 +0000 +++ refind-0.13.2/keys/README.txt 2021-03-07 18:26:35.000000000 +0000 @@ -26,11 +26,16 @@ - canonical-uefi-ca.crt & canonical-uefi-ca.cer -- Canonical's public key, matched to the one used to sign Ubuntu boot loaders and kernels. -- centos.crt & centos.cer -- Public keys used to sign CentOS binaries, taken - from shim-signed-0.9-2.el7.src.rpm. Note that the binary's centos.crt file - was actually in .cer format, and has been renamed appropriately. The - centos.crt file included here is transformed from the original file by - openssl. Tested booting CentOS 7. +- centossecurebootca2.cer & centossecurebootca2.crt -- Public keys used to + authenticate CentOS binaries, taken from shim-15-15.el8_2.src.rpm. Note + that these are new files for CentOS 8; CentOS 7 used a now-expired key. + +- centossecureboot201.cer & centossecureboot201.crt -- I'm not entirely sure + what this is, but it comes from the same shim-15-15.el8_2.src.rpm package + as the preceding files. + +- debian.cer -- Debian's public key, obtained from + https://dsa.debian.org/secure-boot-ca. - fedora-ca.cer & fedora-ca.crt -- Fedora's public key, matched to the one used used to sign Fedora's shim 0.8 binary. diff -Nru refind-0.12.0/libeg/image.c refind-0.13.2/libeg/image.c --- refind-0.12.0/libeg/image.c 2020-03-01 18:27:53.000000000 +0000 +++ refind-0.13.2/libeg/image.c 2021-03-13 00:47:48.000000000 +0000 @@ -63,6 +63,7 @@ #include "../include/refit_call_wrapper.h" #include "lodepng.h" #include "libeg.h" +#include "log.h" #define MAX_FILE_SIZE (1024*1024*1024) @@ -168,15 +169,20 @@ UINTN Offset = 0; UINTN x_ratio, y_ratio, x_diff, y_diff; - if ((Image == NULL) || (Image->Height == 0) || (Image->Width == 0) || (NewWidth == 0) || (NewHeight == 0)) + LOG(3, LOG_LINE_NORMAL, L"Scaling image to %d x %d", NewWidth, NewHeight); + if ((Image == NULL) || (Image->Height == 0) || (Image->Width == 0) || (NewWidth == 0) || (NewHeight == 0)) { + LOG(1, LOG_LINE_NORMAL, L"In egScaleImage(), Image is NULL or a size is 0"); return NULL; + } if ((Image->Width == NewWidth) && (Image->Height == NewHeight)) return (egCopyImage(Image)); NewImage = egCreateImage(NewWidth, NewHeight, Image->HasAlpha); - if (NewImage == NULL) + if (NewImage == NULL) { + LOG(1, LOG_LINE_NORMAL, L"In egScaleImage(), unable to create new image"); return NULL; + } x_ratio = ((Image->Width - 1) * FP_MULTIPLIER) / NewWidth; y_ratio = ((Image->Height - 1) * FP_MULTIPLIER) / NewHeight; @@ -218,6 +224,7 @@ (d.a) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER); } // for (j...) } // for (i...) + LOG(3, LOG_LINE_NORMAL, L"Scaling of image complete"); return NewImage; } // EG_IMAGE * egScaleImage() @@ -246,6 +253,7 @@ if ((BaseDir == NULL) || (FileName == NULL)) return EFI_NOT_FOUND; + LOG(3, LOG_LINE_NORMAL, L"Loading file '%s'", FileName); Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(Status)) { return Status; @@ -311,7 +319,7 @@ } Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (EFI_ERROR(Status)) return Status; @@ -336,13 +344,13 @@ { EG_IMAGE *NewImage = NULL; - NewImage = egDecodeICNS(FileData, FileDataLength, IconSize, WantAlpha); - if (NewImage == NULL) - NewImage = egDecodePNG(FileData, FileDataLength, IconSize, WantAlpha); + NewImage = egDecodePNG(FileData, FileDataLength, IconSize, WantAlpha); if (NewImage == NULL) NewImage = egDecodeJPEG(FileData, FileDataLength, IconSize, WantAlpha); if (NewImage == NULL) NewImage = egDecodeBMP(FileData, FileDataLength, IconSize, WantAlpha); + if (NewImage == NULL) + NewImage = egDecodeICNS(FileData, FileDataLength, IconSize, WantAlpha); return NewImage; } @@ -384,19 +392,23 @@ // load file Status = egLoadFile(BaseDir, Path, &FileData, &FileDataLength); if (EFI_ERROR(Status)) - return NULL; + return NULL; // decode it Image = egDecodeAny(FileData, FileDataLength, IconSize, TRUE); FreePool(FileData); if ((Image->Width != IconSize) || (Image->Height != IconSize)) { - NewImage = egScaleImage(Image, IconSize, IconSize); - if (!NewImage) { - Print(L"Warning: Unable to scale icon from %d x %d to %d x %d from '%s'\n", + NewImage = egScaleImage(Image, IconSize, IconSize); + if (NewImage) { + egFreeImage(Image); + LOG(4, LOG_LINE_NORMAL, L"In egLoadIcon(), have called egFreeImage()"); + Image = NewImage; + } else { + LOG(1, LOG_LINE_NORMAL, L"Warning: Unable to scale icon from %d x %d to %d x %d from '%s'", Image->Width, Image->Height, IconSize, IconSize, Path); - } - egFreeImage(Image); - Image = NewImage; + Print(L"Warning: Unable to scale icon from %d x %d to %d x %d from '%s'\n", + Image->Width, Image->Height, IconSize, IconSize, Path); + } } return Image; @@ -408,18 +420,20 @@ // an image based on "myicons/os_linux.icns" or "myicons/os_linux.png", in that // order of preference. Returns NULL if no such file is a valid icon file. EG_IMAGE * egLoadIconAnyType(IN EFI_FILE *BaseDir, IN CHAR16 *SubdirName, IN CHAR16 *BaseName, IN UINTN IconSize) { - EG_IMAGE *Image = NULL; - CHAR16 *Extension; - CHAR16 FileName[256]; - UINTN i = 0; - - while (((Extension = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) && (Image == NULL)) { - SPrint(FileName, 255, L"%s\\%s.%s", SubdirName, BaseName, Extension); - Image = egLoadIcon(BaseDir, FileName, IconSize); - MyFreePool(Extension); - } // while() + EG_IMAGE *Image = NULL; + CHAR16 *Extension; + CHAR16 *FileName; + UINTN i = 0; + + while (((Extension = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) && (Image == NULL)) { + FileName = PoolPrint(L"%s\\%s.%s", SubdirName, BaseName, Extension); + Image = egLoadIcon(BaseDir, FileName, IconSize); + LOG(4, LOG_LINE_NORMAL, L"Have loaded Image in egLoadIconAnyType()"); + MyFreePool(Extension); + MyFreePool(FileName); + } // while() - return Image; + return Image; } // EG_IMAGE *egLoadIconAnyType() // Returns an icon with any extension in ICON_EXTENSIONS from either the directory @@ -451,6 +465,9 @@ UINTN CompLen; UINTN PixelCount; + if (EmbeddedImage == NULL) + return NULL; + // sanity check if (EmbeddedImage->PixelMode > EG_MAX_EIPIXELMODE || (EmbeddedImage->CompressMode != EG_EICOMPMODE_NONE && EmbeddedImage->CompressMode != EG_EICOMPMODE_RLE)) @@ -533,16 +550,18 @@ IN UINTN AreaPosX, IN UINTN AreaPosY, IN OUT UINTN *AreaWidth, IN OUT UINTN *AreaHeight) { - if (AreaPosX >= Image->Width || AreaPosY >= Image->Height) { - // out of bounds, operation has no effect - *AreaWidth = 0; - *AreaHeight = 0; - } else { - // calculate affected area - if (*AreaWidth > Image->Width - AreaPosX) - *AreaWidth = Image->Width - AreaPosX; - if (*AreaHeight > Image->Height - AreaPosY) - *AreaHeight = Image->Height - AreaPosY; + if (Image && AreaWidth && AreaHeight) { + if (AreaPosX >= Image->Width || AreaPosY >= Image->Height) { + // out of bounds, operation has no effect + *AreaWidth = 0; + *AreaHeight = 0; + } else { + // calculate affected area + if (*AreaWidth > Image->Width - AreaPosX) + *AreaWidth = Image->Width - AreaPosX; + if (*AreaHeight > Image->Height - AreaPosY) + *AreaHeight = Image->Height - AreaPosY; + } } } @@ -552,13 +571,15 @@ EG_PIXEL FillColor; EG_PIXEL *PixelPtr; - FillColor = *Color; - if (!CompImage->HasAlpha) - FillColor.a = 0; - - PixelPtr = CompImage->PixelData; - for (i = 0; i < CompImage->Width * CompImage->Height; i++, PixelPtr++) - *PixelPtr = FillColor; + if (CompImage && Color) { + FillColor = *Color; + if (!CompImage->HasAlpha) + FillColor.a = 0; + + PixelPtr = CompImage->PixelData; + for (i = 0; i < CompImage->Width * CompImage->Height; i++, PixelPtr++) + *PixelPtr = FillColor; + } } VOID egFillImageArea(IN OUT EG_IMAGE *CompImage, @@ -571,19 +592,21 @@ EG_PIXEL *PixelPtr; EG_PIXEL *PixelBasePtr; - egRestrictImageArea(CompImage, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight); - - if (AreaWidth > 0) { - FillColor = *Color; - if (!CompImage->HasAlpha) - FillColor.a = 0; + if (CompImage && Color) { + egRestrictImageArea(CompImage, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight); - PixelBasePtr = CompImage->PixelData + AreaPosY * CompImage->Width + AreaPosX; - for (y = 0; y < AreaHeight; y++) { - PixelPtr = PixelBasePtr; - for (x = 0; x < AreaWidth; x++, PixelPtr++) - *PixelPtr = FillColor; - PixelBasePtr += CompImage->Width; + if (AreaWidth > 0) { + FillColor = *Color; + if (!CompImage->HasAlpha) + FillColor.a = 0; + + PixelBasePtr = CompImage->PixelData + AreaPosY * CompImage->Width + AreaPosX; + for (y = 0; y < AreaHeight; y++) { + PixelPtr = PixelBasePtr; + for (x = 0; x < AreaWidth; x++, PixelPtr++) + *PixelPtr = FillColor; + PixelBasePtr += CompImage->Width; + } } } } @@ -595,15 +618,17 @@ UINTN x, y; EG_PIXEL *TopPtr, *CompPtr; - for (y = 0; y < Height; y++) { - TopPtr = TopBasePtr; - CompPtr = CompBasePtr; - for (x = 0; x < Width; x++) { - *CompPtr = *TopPtr; - TopPtr++, CompPtr++; + if (CompBasePtr && TopBasePtr) { + for (y = 0; y < Height; y++) { + TopPtr = TopBasePtr; + CompPtr = CompBasePtr; + for (x = 0; x < Width; x++) { + *CompPtr = *TopPtr; + TopPtr++, CompPtr++; + } + TopBasePtr += TopLineOffset; + CompBasePtr += CompLineOffset; } - TopBasePtr += TopLineOffset; - CompBasePtr += CompLineOffset; } } @@ -617,27 +642,24 @@ UINTN RevAlpha; UINTN Temp; - for (y = 0; y < Height; y++) { - TopPtr = TopBasePtr; - CompPtr = CompBasePtr; - for (x = 0; x < Width; x++) { - Alpha = TopPtr->a; - RevAlpha = 255 - Alpha; - Temp = (UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha + 0x80; - CompPtr->b = (Temp + (Temp >> 8)) >> 8; - Temp = (UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha + 0x80; - CompPtr->g = (Temp + (Temp >> 8)) >> 8; - Temp = (UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha + 0x80; - CompPtr->r = (Temp + (Temp >> 8)) >> 8; - /* - CompPtr->b = ((UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha) / 255; - CompPtr->g = ((UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha) / 255; - CompPtr->r = ((UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha) / 255; - */ - TopPtr++, CompPtr++; + if (CompBasePtr && TopBasePtr) { + for (y = 0; y < Height; y++) { + TopPtr = TopBasePtr; + CompPtr = CompBasePtr; + for (x = 0; x < Width; x++) { + Alpha = TopPtr->a; + RevAlpha = 255 - Alpha; + Temp = (UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha + 0x80; + CompPtr->b = (Temp + (Temp >> 8)) >> 8; + Temp = (UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha + 0x80; + CompPtr->g = (Temp + (Temp >> 8)) >> 8; + Temp = (UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha + 0x80; + CompPtr->r = (Temp + (Temp >> 8)) >> 8; + TopPtr++, CompPtr++; + } + TopBasePtr += TopLineOffset; + CompBasePtr += CompLineOffset; } - TopBasePtr += TopLineOffset; - CompBasePtr += CompLineOffset; } } @@ -645,18 +667,20 @@ { UINTN CompWidth, CompHeight; - CompWidth = TopImage->Width; - CompHeight = TopImage->Height; - egRestrictImageArea(CompImage, PosX, PosY, &CompWidth, &CompHeight); - - // compose - if (CompWidth > 0) { - if (TopImage->HasAlpha) { - egRawCompose(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData, - CompWidth, CompHeight, CompImage->Width, TopImage->Width); - } else { - egRawCopy(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData, - CompWidth, CompHeight, CompImage->Width, TopImage->Width); + if (CompImage && TopImage) { + CompWidth = TopImage->Width; + CompHeight = TopImage->Height; + egRestrictImageArea(CompImage, PosX, PosY, &CompWidth, &CompHeight); + + // compose + if (CompWidth > 0) { + if (TopImage->HasAlpha) { + egRawCompose(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData, + CompWidth, CompHeight, CompImage->Width, TopImage->Width); + } else { + egRawCopy(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData, + CompWidth, CompHeight, CompImage->Width, TopImage->Width); + } } } } /* VOID egComposeImage() */ @@ -669,9 +693,11 @@ { UINTN i; - for (i = 0; i < PixelCount; i++) { - *DestPlanePtr = *SrcDataPtr++; - DestPlanePtr += 4; + if (SrcDataPtr && DestPlanePtr) { + for (i = 0; i < PixelCount; i++) { + *DestPlanePtr = *SrcDataPtr++; + DestPlanePtr += 4; + } } } @@ -679,9 +705,11 @@ { UINTN i; - for (i = 0; i < PixelCount; i++) { - *DestPlanePtr = Value; - DestPlanePtr += 4; + if (DestPlanePtr) { + for (i = 0; i < PixelCount; i++) { + *DestPlanePtr = Value; + DestPlanePtr += 4; + } } } @@ -689,9 +717,11 @@ { UINTN i; - for (i = 0; i < PixelCount; i++) { - *DestPlanePtr = *SrcPlanePtr; - DestPlanePtr += 4, SrcPlanePtr += 4; + if (SrcPlanePtr && DestPlanePtr) { + for (i = 0; i < PixelCount; i++) { + *DestPlanePtr = *SrcPlanePtr; + DestPlanePtr += 4, SrcPlanePtr += 4; + } } } diff -Nru refind-0.12.0/libeg/lodepng.c refind-0.13.2/libeg/lodepng.c --- refind-0.12.0/libeg/lodepng.c 2020-02-13 03:28:13.000000000 +0000 +++ refind-0.13.2/libeg/lodepng.c 2021-02-27 04:01:39.000000000 +0000 @@ -1,5 +1,5 @@ /* -LodePNG version 20200211 +LodePNG version 20201017 Copyright (c) 2005-2020 Lode Vandevenne @@ -44,7 +44,7 @@ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ -const char* LODEPNG_VERSION_STRING = "20200211"; +const char* LODEPNG_VERSION_STRING = "20201017"; /* This source file is built up in the following large parts. The code sections @@ -113,7 +113,7 @@ #define LODEPNG_RESTRICT /* not available */ #endif -/* Replacements for C library functions memcpy and strlen, to support those platforms +/* Replacements for C library functions such as memcpy and strlen, to support platforms where a full C library is not available. The compiler can recognize them and compile to something as fast. */ @@ -123,11 +123,17 @@ for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; } +static void lodepng_memset(void* LODEPNG_RESTRICT dst, + int value, size_t num) { + size_t i; + for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value; +} + /* does not check memory out of bounds, do not use on untrusted data */ static size_t lodepng_strlen(const char* a) { const char* orig = a; /* avoid warning about unused function in case of disabled COMPILE... macros */ - (void)lodepng_strlen; + (void)(&lodepng_strlen); while(*a) a++; return (size_t)(a - orig); } @@ -169,7 +175,7 @@ out of a loop (to go to the cleanup phase of a function). This macro does that. It makes the error handling code shorter and more readable. -Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); +Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); */ #define CERROR_BREAK(errorvar, code){\ errorvar = code;\ @@ -222,9 +228,10 @@ } /*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_reserve(uivector* p, size_t allocsize) { +static unsigned uivector_resize(uivector* p, size_t size) { + size_t allocsize = size * sizeof(unsigned); if(allocsize > p->allocsize) { - size_t newsize = (allocsize > p->allocsize * 2u) ? allocsize : ((allocsize * 3u) >> 1u); + size_t newsize = allocsize + (p->allocsize >> 1u); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; @@ -232,24 +239,10 @@ } else return 0; /*error: not enough memory*/ } - return 1; -} - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_resize(uivector* p, size_t size) { - if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; p->size = size; return 1; /*success*/ } -/*resize and give all new elements the value*/ -static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) { - size_t oldsize = p->size, i; - if(!uivector_resize(p, size)) return 0; - for(i = oldsize; i < size; ++i) p->data[i] = value; - return 1; -} - static void uivector_init(uivector* p) { p->data = NULL; p->size = p->allocsize = 0; @@ -274,9 +267,9 @@ } ucvector; /*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_reserve(ucvector* p, size_t allocsize) { - if(allocsize > p->allocsize) { - size_t newsize = (allocsize > p->allocsize * 2u) ? allocsize : ((allocsize * 3u) >> 1u); +static unsigned ucvector_resize(ucvector* p, size_t size) { + if(size > p->allocsize) { + size_t newsize = size + (p->allocsize >> 1u); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; @@ -284,48 +277,16 @@ } else return 0; /*error: not enough memory*/ } - return 1; -} - -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_resize(ucvector* p, size_t size) { - if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; p->size = size; return 1; /*success*/ } -#ifdef LODEPNG_COMPILE_PNG - -static void ucvector_cleanup(void* p) { - ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; - lodepng_free(((ucvector*)p)->data); - ((ucvector*)p)->data = NULL; -} - -static void ucvector_init(ucvector* p) { - p->data = NULL; - p->size = p->allocsize = 0; -} -#endif /*LODEPNG_COMPILE_PNG*/ - -#ifdef LODEPNG_COMPILE_ZLIB -/*you can both convert from vector to buffer&size and vice versa. If you use -init_buffer to take over a buffer and size, it is not needed to use cleanup*/ -static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) { - p->data = buffer; - p->allocsize = p->size = size; -} -#endif /*LODEPNG_COMPILE_ZLIB*/ - -#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_push_back(ucvector* p, unsigned char c) { - if(!ucvector_resize(p, p->size + 1)) return 0; - p->data[p->size - 1] = c; - return 1; +static ucvector ucvector_init(unsigned char* buffer, size_t size) { + ucvector v; + v.data = buffer; + v.allocsize = v.size = size; + return v; } -#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ - /* ////////////////////////////////////////////////////////////////////////// */ @@ -338,19 +299,20 @@ *out = NULL; } -/* dynamically allocates a new string with a copy of the null terminated input text */ -static char* alloc_string(const char* in) { - size_t insize = lodepng_strlen(in); +/*also appends null termination character*/ +static char* alloc_string_sized(const char* in, size_t insize) { char* out = (char*)lodepng_malloc(insize + 1); if(out) { - size_t i; - for(i = 0; i != insize; ++i) { - out[i] = in[i]; - } - out[i] = 0; + lodepng_memcpy(out, in, insize); + out[insize] = 0; } return out; } + +/* dynamically allocates a new string with a copy of the null terminated input text */ +static char* alloc_string(const char* in) { + return alloc_string_sized(in, lodepng_strlen(in)); +} #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -409,13 +371,13 @@ readsize = fread(out, 1, size, file); fclose(file); - if (readsize != size) return 78; + if(readsize != size) return 78; return 0; } unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { long size = lodepng_filesize(filename); - if (size < 0) return 78; + if(size < 0) return 78; *outsize = (size_t)size; *out = (unsigned char*)lodepng_malloc((size_t)size); @@ -447,18 +409,21 @@ typedef struct { ucvector* data; - size_t bp; + unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/ } LodePNGBitWriter; -void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) { +static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) { writer->data = data; writer->bp = 0; } /*TODO: this ignores potential out of memory errors*/ -#define WRITEBIT(/*size_t**/ writer, /*unsigned char*/ bit){\ +#define WRITEBIT(writer, bit){\ /* append new byte */\ - if(((writer->bp) & 7u) == 0) ucvector_push_back(writer->data, (unsigned char)0);\ + if(((writer->bp) & 7u) == 0) {\ + if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\ + writer->data->data[writer->data->size - 1] = 0;\ + }\ (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\ ++writer->bp;\ } @@ -468,7 +433,7 @@ if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */ WRITEBIT(writer, value); } else { - /* TODO: increase output size nly once here rather than in each WRITEBIT */ + /* TODO: increase output size only once here rather than in each WRITEBIT */ size_t i; for(i = 0; i != nbits; ++i) { WRITEBIT(writer, (unsigned char)((value >> i) & 1)); @@ -623,7 +588,8 @@ size_t numsteps, const size_t* steps, unsigned* result) { size_t i; LodePNGBitReader reader; - LodePNGBitReader_init(&reader, data, size); + unsigned error = LodePNGBitReader_init(&reader, data, size); + if(error) return 0; for(i = 0; i < numsteps; i++) { size_t step = steps[i]; unsigned ok; @@ -678,8 +644,8 @@ = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; -/*the order in which "code length alphabet code lengths" are stored, out of this -the huffman tree of the dynamic huffman tree lengths is generated*/ +/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman +tree of the dynamic huffman tree lengths is generated*/ static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; @@ -729,7 +695,7 @@ if(!maxlens) return 83; /*alloc fail*/ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ - for(i = 0; i < headsize; ++i) maxlens[i] = 0; + lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); for(i = 0; i < tree->numcodes; i++) { unsigned symbol = tree->codes[i]; unsigned l = tree->lengths[i]; @@ -1019,7 +985,7 @@ } } - for(i = 0; i != numcodes; ++i) lengths[i] = 0; + lodepng_memset(lengths, 0, numcodes * sizeof(*lengths)); /*ensure at least two present symbols. There should be at least one symbol according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To @@ -1079,30 +1045,17 @@ /*Create the Huffman tree given the symbol frequencies*/ static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, size_t mincodes, size_t numcodes, unsigned maxbitlen) { - size_t i; unsigned error = 0; - unsigned* lengths; while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ - lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); - if(!lengths) return 83; /*alloc fail*/ - tree->lengths = lengths; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ - /*initialize all lengths to 0*/ - for(i = 0; i < numcodes; i++) tree->lengths[i] = 0; error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); if(!error) error = HuffmanTree_makeFromLengths2(tree); return error; } - -static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) { - return tree->codes[index]; -} - -static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) { - return tree->lengths[index]; -} #endif /*LODEPNG_COMPILE_ENCODER*/ /*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ @@ -1165,11 +1118,12 @@ /* / Inflator (Decompressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ -/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ -static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { - /*TODO: check for out of memory errors*/ - generateFixedLitLenTree(tree_ll); - generateFixedDistanceTree(tree_d); +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification +Returns error code.*/ +static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { + unsigned error = generateFixedLitLenTree(tree_ll); + if(error) return error; + return generateFixedDistanceTree(tree_d); } /*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ @@ -1220,8 +1174,8 @@ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; - for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; + lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); + lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; @@ -1306,8 +1260,8 @@ } /*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ -static unsigned inflateHuffmanBlock(ucvector* out, size_t* pos, LodePNGBitReader* reader, - unsigned btype) { +static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, + unsigned btype, size_t max_output_size) { unsigned error = 0; HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/ @@ -1315,7 +1269,7 @@ HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); - if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); + if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); while(!error) /*decode all symbols until end reached, breaks at end code*/ { @@ -1324,10 +1278,8 @@ ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ code_ll = huffmanDecodeSymbol(reader, &tree_ll); if(code_ll <= 255) /*literal symbol*/ { - /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ - if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); - out->data[*pos] = (unsigned char)code_ll; - ++(*pos); + if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[out->size - 1] = (unsigned char)code_ll; } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { unsigned code_d, distance; unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ @@ -1363,21 +1315,20 @@ } /*part 5: fill in all the out[n] values based on the length and dist*/ - start = (*pos); + start = out->size; if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; - if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); - if (distance < length) { + if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); + if(distance < length) { size_t forward; - lodepng_memcpy(out->data + *pos, out->data + backward, distance); - *pos += distance; + lodepng_memcpy(out->data + start, out->data + backward, distance); + start += distance; for(forward = distance; forward < length; ++forward) { - out->data[(*pos)++] = out->data[backward++]; + out->data[start++] = out->data[backward++]; } } else { - lodepng_memcpy(out->data + *pos, out->data + backward, length); - *pos += length; + lodepng_memcpy(out->data + start, out->data + backward, length); } } else if(code_ll == 256) { break; /*end code, break the loop*/ @@ -1391,6 +1342,9 @@ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ } + if(max_output_size && out->size > max_output_size) { + ERROR_BREAK(109); /*error, larger than max size*/ + } } HuffmanTree_cleanup(&tree_ll); @@ -1399,8 +1353,8 @@ return error; } -static unsigned inflateNoCompression(ucvector* out, size_t* pos, - LodePNGBitReader* reader, const LodePNGDecompressSettings* settings) { +static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, + const LodePNGDecompressSettings* settings) { size_t bytepos; size_t size = reader->size; unsigned LEN, NLEN, error = 0; @@ -1418,13 +1372,12 @@ return 21; /*error: NLEN is not one's complement of LEN*/ } - if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ + if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ - lodepng_memcpy(out->data + *pos, reader->data + bytepos, LEN); - *pos += LEN; + lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); bytepos += LEN; reader->bp = bytepos << 3u; @@ -1436,7 +1389,6 @@ const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned BFINAL = 0; - size_t pos = 0; /*byte position in the out buffer*/ LodePNGBitReader reader; unsigned error = LodePNGBitReader_init(&reader, in, insize); @@ -1449,10 +1401,10 @@ BTYPE = readBits(&reader, 2); if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ - else if(BTYPE == 0) error = inflateNoCompression(out, &pos, &reader, settings); /*no compression*/ - else error = inflateHuffmanBlock(out, &pos, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ - - if(error) return error; + else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ + else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/ + if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109; + if(error) break; } return error; @@ -1461,22 +1413,27 @@ unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { - unsigned error; - ucvector v; - ucvector_init_buffer(&v, *out, *outsize); - error = lodepng_inflatev(&v, in, insize, settings); + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_inflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } -static unsigned inflate(unsigned char** out, size_t* outsize, - const unsigned char* in, size_t insize, +static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_inflate) { - return settings->custom_inflate(out, outsize, in, insize, settings); + unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); + out->allocsize = out->size; + if(error) { + /*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && out->size > settings->max_output_size) error = 109; + } + return error; } else { - return lodepng_inflate(out, outsize, in, insize, settings); + return lodepng_inflatev(out, in, insize, settings); } } @@ -1499,7 +1456,7 @@ while(left <= right) { size_t mid = (left + right) >> 1; - if (array[mid] >= value) right = mid - 1; + if(array[mid] >= value) right = mid - 1; else left = mid + 1; } if(left >= array_size || array[left] > value) left--; @@ -1518,10 +1475,15 @@ unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); - uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); - uivector_push_back(values, extra_length); - uivector_push_back(values, dist_code); - uivector_push_back(values, extra_distance); + size_t pos = values->size; + /*TODO: return error when this fails (out of memory)*/ + unsigned ok = uivector_resize(values, values->size + 4); + if(ok) { + values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX; + values->data[pos + 1] = extra_length; + values->data[pos + 2] = dist_code; + values->data[pos + 3] = extra_distance; + } } /*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 @@ -1783,31 +1745,30 @@ /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ - size_t i, j, numdeflateblocks = (datasize + 65534u) / 65535u; + size_t i, numdeflateblocks = (datasize + 65534u) / 65535u; unsigned datapos = 0; for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; + size_t pos = out->size; BFINAL = (i == numdeflateblocks - 1); BTYPE = 0; - firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u)); - ucvector_push_back(out, firstbyte); - LEN = 65535; if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; - ucvector_push_back(out, (unsigned char)(LEN & 255)); - ucvector_push_back(out, (unsigned char)(LEN >> 8u)); - ucvector_push_back(out, (unsigned char)(NLEN & 255)); - ucvector_push_back(out, (unsigned char)(NLEN >> 8u)); - - /*Decompressed data*/ - for(j = 0; j < 65535 && datapos < datasize; ++j) { - ucvector_push_back(out, data[datapos++]); - } + if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/ + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u)); + out->data[pos + 0] = firstbyte; + out->data[pos + 1] = (unsigned char)(LEN & 255); + out->data[pos + 2] = (unsigned char)(LEN >> 8u); + out->data[pos + 3] = (unsigned char)(NLEN & 255); + out->data[pos + 4] = (unsigned char)(NLEN >> 8u); + lodepng_memcpy(out->data + pos + 5, data + datapos, LEN); + datapos += LEN; } return 0; @@ -1823,7 +1784,7 @@ size_t i = 0; for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; - writeBitsReversed(writer, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); + writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]); if(val > 256) /*for a length code, 3 more things have to be added*/ { unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; @@ -1836,8 +1797,7 @@ unsigned distance_extra_bits = lz77_encoded->data[++i]; writeBits(writer, length_extra_bits, n_length_extra_bits); - writeBitsReversed(writer, HuffmanTree_getCode(tree_d, distance_code), - HuffmanTree_getLength(tree_d, distance_code)); + writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]); writeBits(writer, distance_extra_bits, n_distance_extra_bits); } } @@ -1865,42 +1825,45 @@ HuffmanTree tree_ll; /*tree for lit,len values*/ HuffmanTree tree_d; /*tree for distance codes*/ HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ - uivector frequencies_ll; /*frequency of lit,len codes*/ - uivector frequencies_d; /*frequency of dist codes*/ - uivector frequencies_cl; /*frequency of code length codes*/ - uivector bitlen_lld; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/ - uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/ - /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl - (these are written as is in the file, it would be crazy to compress these using yet another huffman - tree that needs to be represented by yet another set of code lengths)*/ - uivector bitlen_cl; + unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/ + unsigned* frequencies_d = 0; /*frequency of dist codes*/ + unsigned* frequencies_cl = 0; /*frequency of code length codes*/ + unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/ + unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/ size_t datasize = dataend - datapos; /* - Due to the huffman compression of huffman tree representations ("two levels"), there are some analogies: + If we could call "bitlen_cl" the the code length code lengths ("clcl"), that is the bit lengths of codes to represent + tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are + some analogies: bitlen_lld is to tree_cl what data is to tree_ll and tree_d. bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. */ unsigned BFINAL = final; - size_t numcodes_ll, numcodes_d, i; + size_t i; + size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl; unsigned HLIT, HDIST, HCLEN; uivector_init(&lz77_encoded); HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); HuffmanTree_init(&tree_cl); - uivector_init(&frequencies_ll); - uivector_init(&frequencies_d); - uivector_init(&frequencies_cl); - uivector_init(&bitlen_lld); - uivector_init(&bitlen_lld_e); - uivector_init(&bitlen_cl); + /* could fit on stack, but >1KB is on the larger side so allocate instead */ + frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll)); + frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d)); + frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + + if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/ /*This while loop never loops due to a break at the end, it is here to allow breaking out of it to the cleanup phase on error conditions.*/ while(!error) { + lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll)); + lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d)); + lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + if(settings->use_lz77) { error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); @@ -1910,94 +1873,92 @@ for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } - if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); - if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); - /*Count the frequencies of lit, len and dist codes*/ for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; - ++frequencies_ll.data[symbol]; + ++frequencies_ll[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; - ++frequencies_d.data[dist]; + ++frequencies_d[dist]; i += 3; } } - frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ - error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15); if(error) break; /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ - error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15); if(error) break; - numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; - numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; + numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286); + numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30); /*store the code lengths of both generated trees in bitlen_lld*/ - for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); - for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + numcodes_lld = numcodes_ll + numcodes_d; + bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld)); + /*numcodes_lld_e never needs more size than bitlen_lld*/ + bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e)); + if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/ + numcodes_lld_e = 0; + + for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i]; + for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i]; /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ - for(i = 0; i != (unsigned)bitlen_lld.size; ++i) { + for(i = 0; i != numcodes_lld; ++i) { unsigned j = 0; /*amount of repetitions*/ - while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; + while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j; - if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ { + if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ { ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { - uivector_push_back(&bitlen_lld_e, 17); - uivector_push_back(&bitlen_lld_e, j - 3); + bitlen_lld_e[numcodes_lld_e++] = 17; + bitlen_lld_e[numcodes_lld_e++] = j - 3; } else /*repeat code 18 supports max 138 zeroes*/ { if(j > 138) j = 138; - uivector_push_back(&bitlen_lld_e, 18); - uivector_push_back(&bitlen_lld_e, j - 11); + bitlen_lld_e[numcodes_lld_e++] = 18; + bitlen_lld_e[numcodes_lld_e++] = j - 11; } i += (j - 1); } else if(j >= 3) /*repeat code for value other than zero*/ { size_t k; unsigned num = j / 6u, rest = j % 6u; - uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; for(k = 0; k < num; ++k) { - uivector_push_back(&bitlen_lld_e, 16); - uivector_push_back(&bitlen_lld_e, 6 - 3); + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = 6 - 3; } if(rest >= 3) { - uivector_push_back(&bitlen_lld_e, 16); - uivector_push_back(&bitlen_lld_e, rest - 3); + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = rest - 3; } else j -= rest; i += j; } else /*too short to benefit from repeat code*/ { - uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; } } /*generate tree_cl, the huffmantree of huffmantrees*/ - - if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != bitlen_lld_e.size; ++i) { - ++frequencies_cl.data[bitlen_lld_e.data[i]]; + for(i = 0; i != numcodes_lld_e; ++i) { + ++frequencies_cl[bitlen_lld_e[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ - if(bitlen_lld_e.data[i] >= 16) ++i; + if(bitlen_lld_e[i] >= 16) ++i; } - error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, - frequencies_cl.size, frequencies_cl.size, 7); + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl, + NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7); if(error) break; - if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i != tree_cl.numcodes; ++i) { - /*lengths of code length tree is in the order as specified by deflate*/ - bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); - } - while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) { - /*remove zeros at the end, but minimum size must be 4*/ - if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); + /*compute amount of code-length-code-lengths to output*/ + numcodes_cl = NUM_CODE_LENGTH_CODES; + /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/ + while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) { + numcodes_cl--; } - if(error) break; /* Write everything into the output @@ -2019,35 +1980,34 @@ writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/ /*write the HLIT, HDIST and HCLEN values*/ + /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies + or in the loop for numcodes_cl above, which saves space. */ HLIT = (unsigned)(numcodes_ll - 257); HDIST = (unsigned)(numcodes_d - 1); - HCLEN = (unsigned)bitlen_cl.size - 4; - /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ - while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; + HCLEN = (unsigned)(numcodes_cl - 4); writeBits(writer, HLIT, 5); writeBits(writer, HDIST, 5); writeBits(writer, HCLEN, 4); - /*write the code lengths of the code length alphabet*/ - for(i = 0; i != HCLEN + 4; ++i) writeBits(writer, bitlen_cl.data[i], 3); + /*write the code lengths of the code length alphabet ("bitlen_cl")*/ + for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3); /*write the lengths of the lit/len AND the dist alphabet*/ - for(i = 0; i != bitlen_lld_e.size; ++i) { - writeBitsReversed(writer, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), - HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); + for(i = 0; i != numcodes_lld_e; ++i) { + writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]); /*extra bits of repeat codes*/ - if(bitlen_lld_e.data[i] == 16) writeBits(writer, bitlen_lld_e.data[++i], 2); - else if(bitlen_lld_e.data[i] == 17) writeBits(writer, bitlen_lld_e.data[++i], 3); - else if(bitlen_lld_e.data[i] == 18) writeBits(writer, bitlen_lld_e.data[++i], 7); + if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2); + else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3); + else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7); } /*write the compressed data symbols*/ writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); /*error: the length of the end code 256 must be larger than 0*/ - if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); + if(tree_ll.lengths[256] == 0) ERROR_BREAK(64); /*write the end code*/ - writeBitsReversed(writer, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]); break; /*end of error-while*/ } @@ -2057,12 +2017,11 @@ HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); HuffmanTree_cleanup(&tree_cl); - uivector_cleanup(&frequencies_ll); - uivector_cleanup(&frequencies_d); - uivector_cleanup(&frequencies_cl); - uivector_cleanup(&bitlen_lld_e); - uivector_cleanup(&bitlen_lld); - uivector_cleanup(&bitlen_cl); + lodepng_free(frequencies_ll); + lodepng_free(frequencies_d); + lodepng_free(frequencies_cl); + lodepng_free(bitlen_lld); + lodepng_free(bitlen_lld_e); return error; } @@ -2081,27 +2040,29 @@ HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); - generateFixedLitLenTree(&tree_ll); - generateFixedDistanceTree(&tree_d); + error = generateFixedLitLenTree(&tree_ll); + if(!error) error = generateFixedDistanceTree(&tree_d); + + if(!error) { + writeBits(writer, BFINAL, 1); + writeBits(writer, 1, 1); /*first bit of BTYPE*/ + writeBits(writer, 0, 1); /*second bit of BTYPE*/ - writeBits(writer, BFINAL, 1); - writeBits(writer, 1, 1); /*first bit of BTYPE*/ - writeBits(writer, 0, 1); /*second bit of BTYPE*/ - - if(settings->use_lz77) /*LZ77 encoded*/ { - uivector lz77_encoded; - uivector_init(&lz77_encoded); - error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, - settings->minmatch, settings->nicematch, settings->lazymatching); - if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); - uivector_cleanup(&lz77_encoded); - } else /*no LZ77, but still will be Huffman compressed*/ { - for(i = datapos; i < dataend; ++i) { - writeBitsReversed(writer, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); + if(settings->use_lz77) /*LZ77 encoded*/ { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } else /*no LZ77, but still will be Huffman compressed*/ { + for(i = datapos; i < dataend; ++i) { + writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]); + } } + /*add END code*/ + if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]); } - /*add END code*/ - if(!error) writeBitsReversed(writer, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); /*cleanup*/ HuffmanTree_cleanup(&tree_ll); @@ -2154,10 +2115,8 @@ unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { - unsigned error; - ucvector v; - ucvector_init_buffer(&v, *out, *outsize); - error = lodepng_deflatev(&v, in, insize, settings); + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_deflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; @@ -2167,7 +2126,9 @@ const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_deflate) { - return settings->custom_deflate(out, outsize, in, insize, settings); + unsigned error = settings->custom_deflate(out, outsize, in, insize, settings); + /*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; } else { return lodepng_deflate(out, outsize, in, insize, settings); } @@ -2210,8 +2171,9 @@ #ifdef LODEPNG_COMPILE_DECODER -unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) { +static unsigned lodepng_zlib_decompressv(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { unsigned error = 0; unsigned CM, CINFO, FDICT; @@ -2238,25 +2200,52 @@ return 26; } - error = inflate(out, outsize, in + 2, insize - 2, settings); + error = inflatev(out, in + 2, insize - 2, settings); if(error) return error; if(!settings->ignore_adler32) { unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); - unsigned checksum = adler32(*out, (unsigned)(*outsize)); + unsigned checksum = adler32(out->data, (unsigned)(out->size)); if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ } return 0; /*no error*/ } -static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) { + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { + unsigned error; if(settings->custom_zlib) { - return settings->custom_zlib(out, outsize, in, insize, settings); + error = settings->custom_zlib(out, outsize, in, insize, settings); + if(error) { + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && *outsize > settings->max_output_size) error = 109; + } } else { - return lodepng_zlib_decompress(out, outsize, in, insize, settings); + ucvector v = ucvector_init(*out, *outsize); + if(expected_size) { + /*reserve the memory to avoid intermediate reallocations*/ + ucvector_resize(&v, *outsize + expected_size); + v.size = *outsize; + } + error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; } + return error; } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2304,7 +2293,9 @@ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_zlib) { - return settings->custom_zlib(out, outsize, in, insize, settings); + unsigned error = settings->custom_zlib(out, outsize, in, insize, settings); + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; } else { return lodepng_zlib_compress(out, outsize, in, insize, settings); } @@ -2315,9 +2306,10 @@ #else /*no LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DECODER -static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, - size_t insize, const LodePNGDecompressSettings* settings) { +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + (void)expected_size; return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2362,13 +2354,14 @@ void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { settings->ignore_adler32 = 0; settings->ignore_nlen = 0; + settings->max_output_size = 0; settings->custom_zlib = 0; settings->custom_inflate = 0; settings->custom_context = 0; } -const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0}; +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0}; #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2568,18 +2561,18 @@ } } -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) { +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) { unsigned i; size_t total_chunk_length, new_length; unsigned char *chunk_start, *new_buffer; if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77; - if(lodepng_addofl(*outlength, total_chunk_length, &new_length)) return 77; + if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77; new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; - (*outlength) = new_length; + (*outsize) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; @@ -2587,30 +2580,36 @@ return 0; } -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, - const char* type, const unsigned char* data) { - unsigned i; - unsigned char *chunk, *new_buffer; - size_t new_length = *outlength; +/*Sets length and name and allocates the space for data and crc but does not +set data or crc yet. Returns the start of the chunk in chunk. The start of +the data is at chunk + 8. To finalize chunk, add the data, then use +lodepng_chunk_generate_crc */ +static unsigned lodepng_chunk_init(unsigned char** chunk, + ucvector* out, + unsigned length, const char* type) { + size_t new_length = out->size; if(lodepng_addofl(new_length, length, &new_length)) return 77; if(lodepng_addofl(new_length, 12, &new_length)) return 77; - new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); - if(!new_buffer) return 83; /*alloc fail*/ - (*out) = new_buffer; - (*outlength) = new_length; - chunk = &(*out)[(*outlength) - length - 12]; + if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/ + *chunk = out->data + new_length - length - 12u; /*1: length*/ - lodepng_set32bitInt(chunk, (unsigned)length); + lodepng_set32bitInt(*chunk, length); /*2: chunk name (4 letters)*/ - chunk[4] = (unsigned char)type[0]; - chunk[5] = (unsigned char)type[1]; - chunk[6] = (unsigned char)type[2]; - chunk[7] = (unsigned char)type[3]; + lodepng_memcpy(*chunk + 4, type, 4); + + return 0; +} + +/* like lodepng_chunk_create but with custom allocsize */ +static unsigned lodepng_chunk_createv(ucvector* out, + unsigned length, const char* type, const unsigned char* data) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type)); /*3: the data*/ - for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; + lodepng_memcpy(chunk + 8, data, length); /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); @@ -2618,6 +2617,15 @@ return 0; } +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, + unsigned length, const char* type, const unsigned char* data) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_chunk_createv(&v, length, type, data); + *out = v.data; + *outsize = v.size; + return error; +} + /* ////////////////////////////////////////////////////////////////////////// */ /* / Color types, channels, bits / */ /* ////////////////////////////////////////////////////////////////////////// */ @@ -2631,6 +2639,7 @@ case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ default: return 31; /* invalid color type */ } return 0; /*allowed color type / bits combination*/ @@ -2643,6 +2652,7 @@ case LCT_PALETTE: return 1; case LCT_GREY_ALPHA: return 2; case LCT_RGBA: return 4; + case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ default: return 0; /*invalid color type*/ } } @@ -2686,13 +2696,12 @@ } unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { - size_t i; lodepng_color_mode_cleanup(dest); - *dest = *source; + lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); if(source->palette) { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ - for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; + lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); } return 0; } @@ -2792,18 +2801,18 @@ #ifdef LODEPNG_COMPILE_PNG -#ifdef LODEPNG_COMPILE_DECODER /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, and in addition has one extra byte per line: the filter byte. So this gives a larger -result than lodepng_get_raw_size. */ -static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { - size_t bpp = lodepng_get_bpp(color); - /* + 1 for the filter byte, and possibly plus padding bits per line */ +result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) { + /* + 1 for the filter byte, and possibly plus padding bits per line. */ + /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; return (size_t)h * line; } +#ifdef LODEPNG_COMPILE_DECODER /*Safely checks whether size_t overflow can be caused due to amount of pixels. This check is overcautious rather than precise. If this check indicates no overflow, you can safely compute in a size_t (but not an unsigned): @@ -2884,8 +2893,8 @@ static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; - dest->text_keys = 0; - dest->text_strings = 0; + dest->text_keys = NULL; + dest->text_strings = NULL; dest->text_num = 0; for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); @@ -2893,29 +2902,31 @@ return 0; } -void lodepng_clear_text(LodePNGInfo* info) { - LodePNGText_cleanup(info); -} - -unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { +static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) { char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); - if(!new_keys || !new_strings) { - lodepng_free(new_keys); - lodepng_free(new_strings); - return 83; /*alloc fail*/ - } - ++info->text_num; - info->text_keys = new_keys; - info->text_strings = new_strings; + if(new_keys) info->text_keys = new_keys; + if(new_strings) info->text_strings = new_strings; + if(!new_keys || !new_strings) return 83; /*alloc fail*/ + + ++info->text_num; info->text_keys[info->text_num - 1] = alloc_string(key); - info->text_strings[info->text_num - 1] = alloc_string(str); + info->text_strings[info->text_num - 1] = alloc_string_sized(str, size); + if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/ return 0; } +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { + return lodepng_add_text_sized(info, key, str, lodepng_strlen(str)); +} + +void lodepng_clear_text(LodePNGInfo* info) { + LodePNGText_cleanup(info); +} + /******************************************************************************/ static void LodePNGIText_init(LodePNGInfo* info) { @@ -2942,10 +2953,10 @@ static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; - dest->itext_keys = 0; - dest->itext_langtags = 0; - dest->itext_transkeys = 0; - dest->itext_strings = 0; + dest->itext_keys = NULL; + dest->itext_langtags = NULL; + dest->itext_transkeys = NULL; + dest->itext_strings = NULL; dest->itext_num = 0; for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], @@ -2958,34 +2969,35 @@ LodePNGIText_cleanup(info); } -unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, - const char* transkey, const char* str) { +static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str, size_t size) { char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); - if(!new_keys || !new_langtags || !new_transkeys || !new_strings) { - lodepng_free(new_keys); - lodepng_free(new_langtags); - lodepng_free(new_transkeys); - lodepng_free(new_strings); - return 83; /*alloc fail*/ - } + + if(new_keys) info->itext_keys = new_keys; + if(new_langtags) info->itext_langtags = new_langtags; + if(new_transkeys) info->itext_transkeys = new_transkeys; + if(new_strings) info->itext_strings = new_strings; + + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/ ++info->itext_num; - info->itext_keys = new_keys; - info->itext_langtags = new_langtags; - info->itext_transkeys = new_transkeys; - info->itext_strings = new_strings; info->itext_keys[info->itext_num - 1] = alloc_string(key); info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); - info->itext_strings[info->itext_num - 1] = alloc_string(str); + info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size); return 0; } +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) { + return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str)); +} + /* same as set but does not delete */ static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { if(profile_size == 0) return 100; /*invalid ICC profile size*/ @@ -3057,7 +3069,7 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { lodepng_info_cleanup(dest); - *dest = *source; + lodepng_memcpy(dest, source, sizeof(LodePNGInfo)); lodepng_color_mode_init(&dest->color); CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); @@ -3101,8 +3113,7 @@ }; static void color_tree_init(ColorTree* tree) { - int i; - for(i = 0; i != 16; ++i) tree->children[i] = 0; + lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); tree->index = -1; } @@ -3524,7 +3535,7 @@ if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - for(i = 0; i != numbytes; ++i) out[i] = in[i]; + lodepng_memcpy(out, in, numbytes); return 0; } @@ -3541,9 +3552,9 @@ /*if the input was also palette with same bitdepth, then the color types are also equal, so copy literally. This to preserve the exact indices that were in the PNG even in case there are duplicate colors in the palette.*/ - if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { + if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - for(i = 0; i != numbytes; ++i) out[i] = in[i]; + lodepng_memcpy(out, in, numbytes); return 0; } } @@ -3721,7 +3732,7 @@ /*Check if the 16-bit input is truly 16-bit*/ if(mode_in->bitdepth == 16 && !sixteen) { - unsigned short r, g, b, a; + unsigned short r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || @@ -4179,7 +4190,7 @@ } } break; - default: return 36; /*error: nonexistent filter type given*/ + default: return 36; /*error: invalid filter type given*/ } return 0; } @@ -4198,7 +4209,8 @@ /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7u) / 8u; - size_t linebytes = (w * bpp + 7u) / 8u; + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; @@ -4238,7 +4250,8 @@ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; - size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } @@ -4253,7 +4266,7 @@ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); @@ -4418,7 +4431,6 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { unsigned error = 0; char *key = 0, *str = 0; - unsigned i; while(!error) /*not really a while loop, only used to break on error*/ { unsigned length, string2_begin; @@ -4432,8 +4444,8 @@ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(key, data, length); key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; string2_begin = length + 1; /*skip keyword null terminator*/ @@ -4441,8 +4453,8 @@ str = (char*)lodepng_malloc(length + 1); if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(str, data + string2_begin, length); str[length] = 0; - for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; error = lodepng_add_text(info, key, str); @@ -4456,16 +4468,17 @@ } /*compressed text chunk (zTXt)*/ -static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, const unsigned char* data, size_t chunkLength) { unsigned error = 0; - unsigned i; + + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; unsigned length, string2_begin; char *key = 0; - ucvector decoded; - - ucvector_init(&decoded); + unsigned char* str = 0; + size_t size = 0; while(!error) /*not really a while loop, only used to break on error*/ { for(length = 0; length < chunkLength && data[length] != 0; ++length) ; @@ -4475,8 +4488,8 @@ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(key, data, length); key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ @@ -4484,34 +4497,34 @@ if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ length = (unsigned)chunkLength - string2_begin; + zlibsettings.max_output_size = decoder->max_text_size; /*will fail if zlib error, e.g. if length is too small*/ - error = zlib_decompress(&decoded.data, &decoded.size, - &data[string2_begin], - length, zlibsettings); + error = zlib_decompress(&str, &size, 0, &data[string2_begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; if(error) break; - ucvector_push_back(&decoded, 0); - - error = lodepng_add_text(info, key, (char*)decoded.data); - + error = lodepng_add_text_sized(info, key, (char*)str, size); break; } lodepng_free(key); - ucvector_cleanup(&decoded); + lodepng_free(str); return error; } /*international text chunk (iTXt)*/ -static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; - ucvector decoded; - ucvector_init(&decoded); /* TODO: only use in case of compressed text */ while(!error) /*not really a while loop, only used to break on error*/ { /*Quick check if the chunk length isn't too small. Even without check @@ -4526,8 +4539,8 @@ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(key, data, length); key[length] = 0; - for(i = 0; i != length; ++i) key[i] = (char)data[i]; /*read the compression method*/ compressed = data[length + 1]; @@ -4544,8 +4557,8 @@ langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(langtag, data + begin, length); langtag[length] = 0; - for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; /*read the transkey*/ begin += length + 1; @@ -4555,8 +4568,8 @@ transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + lodepng_memcpy(transkey, data + begin, length); transkey[length] = 0; - for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; /*read the actual text*/ begin += length + 1; @@ -4564,29 +4577,26 @@ length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; if(compressed) { + unsigned char* str = 0; + size_t size = 0; + zlibsettings.max_output_size = decoder->max_text_size; /*will fail if zlib error, e.g. if length is too small*/ - error = zlib_decompress(&decoded.data, &decoded.size, - &data[begin], - length, zlibsettings); - if(error) break; - if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; - ucvector_push_back(&decoded, 0); + error = zlib_decompress(&str, &size, 0, &data[begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; + if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); + lodepng_free(str); } else { - if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); - - decoded.data[length] = 0; - for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; + error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length); } - error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); - break; } lodepng_free(key); lodepng_free(langtag); lodepng_free(transkey); - ucvector_cleanup(&decoded); return error; } @@ -4650,13 +4660,15 @@ return 0; /* OK */ } -static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, +static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; + size_t size = 0; + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; unsigned length, string2_begin; - ucvector decoded; info->iccp_defined = 1; if(info->iccp_name) lodepng_clear_icc(info); @@ -4677,24 +4689,14 @@ if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ length = (unsigned)chunkLength - string2_begin; - ucvector_init(&decoded); - error = zlib_decompress(&decoded.data, &decoded.size, + zlibsettings.max_output_size = decoder->max_icc_size; + error = zlib_decompress(&info->iccp_profile, &size, 0, &data[string2_begin], - length, zlibsettings); - if(!error) { - if(decoded.size) { - info->iccp_profile_size = decoded.size; - info->iccp_profile = (unsigned char*)lodepng_malloc(decoded.size); - if(info->iccp_profile) { - lodepng_memcpy(info->iccp_profile, decoded.data, decoded.size); - } else { - error = 83; /* alloc fail */ - } - } else { - error = 100; /*invalid ICC profile size*/ - } - } - ucvector_cleanup(&decoded); + length, &zlibsettings); + /*error: ICC profile larger than decoder->max_icc_size*/ + if(error && size > zlibsettings.max_output_size) error = 113; + info->iccp_profile_size = size; + if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ @@ -4707,7 +4709,7 @@ unsigned unhandled = 0; unsigned error = 0; - if (pos + 4 > insize) return 30; + if(pos + 4 > insize) return 30; chunkLength = lodepng_chunk_length(chunk); if(chunkLength > 2147483647) return 63; data = lodepng_chunk_data_const(chunk); @@ -4723,9 +4725,9 @@ } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { error = readChunk_tEXt(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { - error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { - error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "tIME")) { error = readChunk_tIME(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { @@ -4737,7 +4739,7 @@ } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { error = readChunk_sRGB(&state->info_png, data, chunkLength); } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { - error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else { /* unhandled chunk is ok (is not an error) */ @@ -4757,8 +4759,8 @@ const unsigned char* in, size_t insize) { unsigned char IEND = 0; const unsigned char* chunk; - size_t i; - ucvector idat; /*the data from idat chunks*/ + unsigned char* idat; /*the data from idat chunks, zlib compressed*/ + size_t idatsize = 0; unsigned char* scanlines = 0; size_t scanlines_size = 0, expected_size = 0; size_t outsize = 0; @@ -4781,7 +4783,10 @@ CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ } - ucvector_init(&idat); + /*the input filesize is a safe upper bound for the sum of idat chunks size*/ + idat = (unsigned char*)lodepng_malloc(insize); + if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ + chunk = &in[33]; /*first byte of the first chunk after the header*/ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. @@ -4814,11 +4819,11 @@ /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { - size_t oldsize = idat.size; size_t newsize; - if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); - if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/); - for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; + if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(newsize > insize) CERROR_BREAK(state->error, 95); + lodepng_memcpy(idat + idatsize, data, chunkLength); + idatsize += chunkLength; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ @@ -4852,13 +4857,13 @@ } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { /*compressed text chunk (zTXt)*/ if(state->decoder.read_text_chunks) { - state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { /*international text chunk (iTXt)*/ if(state->decoder.read_text_chunks) { - state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "tIME")) { @@ -4877,7 +4882,7 @@ state->error = readChunk_sRGB(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { - state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); if(state->error) break; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { @@ -4903,42 +4908,33 @@ if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); } - if (state->info_png.color.colortype == LCT_PALETTE - && !state->info_png.color.palette) { + if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ } - /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. - If the decompressed size does not match the prediction, the image must be corrupt.*/ - if(state->info_png.interlace_method == 0) { - expected_size = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color); - } else { - /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ - const LodePNGColorMode* color = &state->info_png.color; - expected_size = 0; - expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color); - if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color); - expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color); - if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color); - expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color); - if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color); - expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color); - } if(!state->error) { - /* This allocated data will be realloced by zlib_decompress, initially at - smaller size again. But the fact that it's already allocated at full size - here speeds the multiple reallocs up. TODO: make zlib_decompress support - receiving already allocated buffer with expected size instead. */ - scanlines = (unsigned char*)lodepng_malloc(expected_size); - if(!scanlines) state->error = 83; /*alloc fail*/ - scanlines_size = 0; - } - if(!state->error) { - state->error = zlib_decompress(&scanlines, &scanlines_size, idat.data, - idat.size, &state->decoder.zlibsettings); - if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); + } else { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ + expected_size = 0; + expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); + if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); + if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); + if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); + } + + state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); } - ucvector_cleanup(&idat); + if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ + lodepng_free(idat); if(!state->error) { outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); @@ -4946,7 +4942,7 @@ if(!*out) state->error = 83; /*alloc fail*/ } if(!state->error) { - for(i = 0; i < outsize; i++) (*out)[i] = 0; + lodepng_memset(*out, 0, outsize); state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); } lodepng_free(scanlines); @@ -4996,6 +4992,11 @@ lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*disable reading things that this function doesn't output*/ + state.decoder.read_text_chunks = 0; + state.decoder.remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ error = lodepng_decode(out, w, h, &state, in, insize); lodepng_state_cleanup(&state); return error; @@ -5038,6 +5039,8 @@ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->read_text_chunks = 1; settings->remember_unknown_chunks = 0; + settings->max_text_size = 16777216; + settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; settings->ignore_critical = 0; @@ -5083,28 +5086,21 @@ /* / PNG Encoder / */ /* ////////////////////////////////////////////////////////////////////////// */ -/*chunkName must be string of 4 characters*/ -static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) { - CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); - out->allocsize = out->size; /*fix the allocsize again*/ - return 0; -} -static void writeSignature(ucvector* out) { +static unsigned writeSignature(ucvector* out) { + size_t pos = out->size; + const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10}; /*8 bytes PNG signature, aka the magic bytes*/ - ucvector_push_back(out, 137); - ucvector_push_back(out, 80); - ucvector_push_back(out, 78); - ucvector_push_back(out, 71); - ucvector_push_back(out, 13); - ucvector_push_back(out, 10); - ucvector_push_back(out, 26); - ucvector_push_back(out, 10); + if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/ + lodepng_memcpy(out->data + pos, signature, 8); + return 0; } static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { - unsigned char data[13]; + unsigned char *chunk, *data; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR")); + data = chunk + 8; lodepng_set32bitInt(data + 0, w); /*width*/ lodepng_set32bitInt(data + 4, h); /*height*/ @@ -5114,244 +5110,267 @@ data[11] = 0; /*filter method*/ data[12] = interlace_method; /*interlace method*/ - return addChunk(out, "IHDR", data, sizeof(data)); + lodepng_chunk_generate_crc(chunk); + return 0; } +/* only adds the chunk if needed (there is a key or palette with alpha) */ static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { - unsigned error = 0; - size_t i; - ucvector PLTE; - ucvector_init(&PLTE); - for(i = 0; i != info->palettesize * 4; ++i) { + unsigned char* chunk; + size_t i, j = 8; + + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE")); + + for(i = 0; i != info->palettesize; ++i) { /*add all channels except alpha channel*/ - if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); + chunk[j++] = info->palette[i * 4 + 0]; + chunk[j++] = info->palette[i * 4 + 1]; + chunk[j++] = info->palette[i * 4 + 2]; } - error = addChunk(out, "PLTE", PLTE.data, PLTE.size); - ucvector_cleanup(&PLTE); - return error; + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { - unsigned error = 0; - size_t i; - ucvector tRNS; - ucvector_init(&tRNS); + unsigned char* chunk = 0; + if(info->colortype == LCT_PALETTE) { - size_t amount = info->palettesize; + size_t i, amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ for(i = info->palettesize; i != 0; --i) { - if(info->palette[4 * (i - 1) + 3] == 255) --amount; - else break; + if(info->palette[4 * (i - 1) + 3] != 255) break; + --amount; + } + if(amount) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS")); + /*add the alpha channel values from the palette*/ + for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3]; } - /*add only alpha channel*/ - for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); } else if(info->colortype == LCT_GREY) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); + chunk[10] = (unsigned char)(info->key_g >> 8); + chunk[11] = (unsigned char)(info->key_g & 255); + chunk[12] = (unsigned char)(info->key_b >> 8); + chunk[13] = (unsigned char)(info->key_b & 255); } } - error = addChunk(out, "tRNS", tRNS.data, tRNS.size); - ucvector_cleanup(&tRNS); - - return error; + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, LodePNGCompressSettings* zlibsettings) { - ucvector zlibdata; unsigned error = 0; + unsigned char* zlib = 0; + size_t zlibsize = 0; - /*compress with the Zlib compressor*/ - ucvector_init(&zlibdata); - error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); - if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); - ucvector_cleanup(&zlibdata); - + error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings); + if(!error) { + error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib); + } + lodepng_free(zlib); return error; } static unsigned addChunk_IEND(ucvector* out) { - unsigned error = 0; - error = addChunk(out, "IEND", 0, 0); - return error; + return lodepng_chunk_createv(out, 0, "IEND", 0); } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { - unsigned error = 0; - size_t i; - ucvector text; - ucvector_init(&text); - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&text, 0); /*0 termination char*/ - for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); - error = addChunk(out, "tEXt", text.data, text.size); - ucvector_cleanup(&text); - - return error; + unsigned char* chunk = 0; + size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring); + size_t size = keysize + 1 + textsize; + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt")); + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + lodepng_memcpy(chunk + 9 + keysize, textstring, textsize); + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; - ucvector data, compressed; - size_t i, textsize = lodepng_strlen(textstring); + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword); + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ - ucvector_init(&data); - ucvector_init(&compressed); - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&data, 0); /*0 termination char*/ - ucvector_push_back(&data, 0); /*compression method: 0*/ - - error = zlib_compress(&compressed.data, &compressed.size, + error = zlib_compress(&compressed, &compressedsize, (const unsigned char*)textstring, textsize, zlibsettings); if(!error) { - for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); - error = addChunk(out, "zTXt", data.data, data.size); + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "zTXt"); + } + if(!error) { + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); } - ucvector_cleanup(&compressed); - ucvector_cleanup(&data); + lodepng_free(compressed); return error; } -static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, +static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag, const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; - ucvector data; - size_t i, textsize = lodepng_strlen(textstring); + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey); - ucvector_init(&data); + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ - for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&data, 0); /*null termination char*/ - ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ - ucvector_push_back(&data, 0); /*compression method*/ - for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); - ucvector_push_back(&data, 0); /*null termination char*/ - for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); - ucvector_push_back(&data, 0); /*null termination char*/ - - if(compressed) { - ucvector compressed_data; - ucvector_init(&compressed_data); - error = zlib_compress(&compressed_data.data, &compressed_data.size, + if(compress) { + error = zlib_compress(&compressed, &compressedsize, (const unsigned char*)textstring, textsize, zlibsettings); - if(!error) { - for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); + } + if(!error) { + size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize); + error = lodepng_chunk_init(&chunk, out, size, "iTXt"); + } + if(!error) { + size_t pos = 8; + lodepng_memcpy(chunk + pos, keyword, keysize); + pos += keysize; + chunk[pos++] = 0; /*null termination char*/ + chunk[pos++] = (compress ? 1 : 0); /*compression flag*/ + chunk[pos++] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + pos, langtag, langsize); + pos += langsize; + chunk[pos++] = 0; /*null termination char*/ + lodepng_memcpy(chunk + pos, transkey, transsize); + pos += transsize; + chunk[pos++] = 0; /*null termination char*/ + if(compress) { + lodepng_memcpy(chunk + pos, compressed, compressedsize); + } else { + lodepng_memcpy(chunk + pos, textstring, textsize); } - ucvector_cleanup(&compressed_data); - } else /*not compressed*/ { - for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); + lodepng_chunk_generate_crc(chunk); } - if(!error) error = addChunk(out, "iTXt", data.data, data.size); - ucvector_cleanup(&data); + lodepng_free(compressed); return error; } static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { - unsigned char data[6]; - size_t size = 0; + unsigned char* chunk = 0; if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { - data[0] = (unsigned char)(info->background_r >> 8); - data[1] = (unsigned char)(info->background_r & 255); - size = 2; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { - data[0] = (unsigned char)(info->background_r >> 8); - data[1] = (unsigned char)(info->background_r & 255); - data[2] = (unsigned char)(info->background_g >> 8); - data[3] = (unsigned char)(info->background_g & 255); - data[4] = (unsigned char)(info->background_b >> 8); - data[5] = (unsigned char)(info->background_b & 255); - size = 6; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); + chunk[10] = (unsigned char)(info->background_g >> 8); + chunk[11] = (unsigned char)(info->background_g & 255); + chunk[12] = (unsigned char)(info->background_b >> 8); + chunk[13] = (unsigned char)(info->background_b & 255); } else if(info->color.colortype == LCT_PALETTE) { - data[0] =(unsigned char)(info->background_r & 255); /*palette index*/ - size = 1; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD")); + chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/ } - return addChunk(out, "bKGD", data, size); + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { - unsigned char data[7]; - data[0] = (unsigned char)(time->year >> 8); - data[1] = (unsigned char)(time->year & 255); - data[2] = (unsigned char)time->month; - data[3] = (unsigned char)time->day; - data[4] = (unsigned char)time->hour; - data[5] = (unsigned char)time->minute; - data[6] = (unsigned char)time->second; - return addChunk(out, "tIME", data, sizeof(data)); + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME")); + chunk[8] = (unsigned char)(time->year >> 8); + chunk[9] = (unsigned char)(time->year & 255); + chunk[10] = (unsigned char)time->month; + chunk[11] = (unsigned char)time->day; + chunk[12] = (unsigned char)time->hour; + chunk[13] = (unsigned char)time->minute; + chunk[14] = (unsigned char)time->second; + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { - unsigned char data[9]; - lodepng_set32bitInt(data + 0, info->phys_x); - lodepng_set32bitInt(data + 4, info->phys_y); data[8] = info->phys_unit; - return addChunk(out, "pHYs", data, sizeof(data)); + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs")); + lodepng_set32bitInt(chunk + 8, info->phys_x); + lodepng_set32bitInt(chunk + 12, info->phys_y); + chunk[16] = info->phys_unit; + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) { - unsigned char data[4]; - lodepng_set32bitInt(data, info->gama_gamma); - return addChunk(out, "gAMA", data, sizeof(data)); + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA")); + lodepng_set32bitInt(chunk + 8, info->gama_gamma); + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) { - unsigned char data[32]; - lodepng_set32bitInt(data + 0, info->chrm_white_x); - lodepng_set32bitInt(data + 4, info->chrm_white_y); - lodepng_set32bitInt(data + 8, info->chrm_red_x); - lodepng_set32bitInt(data + 12, info->chrm_red_y); - lodepng_set32bitInt(data + 16, info->chrm_green_x); - lodepng_set32bitInt(data + 20, info->chrm_green_y); - lodepng_set32bitInt(data + 24, info->chrm_blue_x); - lodepng_set32bitInt(data + 28, info->chrm_blue_y); - return addChunk(out, "cHRM", data, sizeof(data)); + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM")); + lodepng_set32bitInt(chunk + 8, info->chrm_white_x); + lodepng_set32bitInt(chunk + 12, info->chrm_white_y); + lodepng_set32bitInt(chunk + 16, info->chrm_red_x); + lodepng_set32bitInt(chunk + 20, info->chrm_red_y); + lodepng_set32bitInt(chunk + 24, info->chrm_green_x); + lodepng_set32bitInt(chunk + 28, info->chrm_green_y); + lodepng_set32bitInt(chunk + 32, info->chrm_blue_x); + lodepng_set32bitInt(chunk + 36, info->chrm_blue_y); + lodepng_chunk_generate_crc(chunk); + return 0; } static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) { unsigned char data = info->srgb_intent; - return addChunk(out, "sRGB", &data, 1); + return lodepng_chunk_createv(out, 1, "sRGB", &data); } static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; - ucvector data, compressed; - size_t i; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t keysize = lodepng_strlen(info->iccp_name); - ucvector_init(&data); - ucvector_init(&compressed); - for(i = 0; info->iccp_name[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)info->iccp_name[i]); - if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ - ucvector_push_back(&data, 0); /*0 termination char*/ - ucvector_push_back(&data, 0); /*compression method: 0*/ - - error = zlib_compress(&compressed.data, &compressed.size, + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + error = zlib_compress(&compressed, &compressedsize, info->iccp_profile, info->iccp_profile_size, zlibsettings); if(!error) { - for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); - error = addChunk(out, "iCCP", data.data, data.size); + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "iCCP"); + } + if(!error) { + lodepng_memcpy(chunk + 8, info->iccp_name, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); } - ucvector_cleanup(&compressed); - ucvector_cleanup(&data); + lodepng_free(compressed); return error; } @@ -5397,17 +5416,18 @@ for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; - default: return; /*nonexistent filter type given*/ + default: return; /*invalid filter type given*/ } } -/* integer binary logarithm */ +/* integer binary logarithm, max return value is 31 */ static size_t ilog2(size_t i) { size_t result = 0; - while(i >= 65536) { result += 16; i >>= 16; } - while(i >= 256) { result += 8; i >>= 8; } - while(i >= 16) { result += 4; i >>= 4; } - while(i >= 2) { result += 1; i >>= 1; } + if(i >= 65536) { result += 16; i >>= 16; } + if(i >= 256) { result += 8; i >>= 8; } + if(i >= 16) { result += 4; i >>= 4; } + if(i >= 4) { result += 2; i >>= 2; } + if(i >= 2) { result += 1; /*i >>= 1;*/ } return result; } @@ -5422,16 +5442,17 @@ } static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, - const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) { + const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) { /* For PNG filter method 0 out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are the scanlines with 1 extra byte per scanline */ - unsigned bpp = lodepng_get_bpp(info); + unsigned bpp = lodepng_get_bpp(color); /*the width of a scanline in bytes, not including the filter type*/ - size_t linebytes = (w * bpp + 7u) / 8u; + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7u) / 8u; const unsigned char* prevline = 0; @@ -5453,7 +5474,7 @@ heuristic is used. */ if(settings->filter_palette_zero && - (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; + (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO; if(bpp == 0) return 31; /*error: invalid color type*/ @@ -5530,7 +5551,7 @@ for(type = 0; type != 5; ++type) { size_t sum = 0; filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); - for(x = 0; x != 256; ++x) count[x] = 0; + lodepng_memset(count, 0, 256 * sizeof(*count)); for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; ++count[type]; /*the filter type itself is part of the scanline*/ for(x = 0; x != 256; ++x) { @@ -5570,7 +5591,8 @@ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; - LodePNGCompressSettings zlibsettings = settings->zlibsettings; + LodePNGCompressSettings zlibsettings; + lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings)); /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, to simulate the true case where the tree is the same for the whole image. Sometimes it gives better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare @@ -5757,29 +5779,6 @@ return error; } -/* -palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... -returns 0 if the palette is opaque, -returns 1 if the palette has a single color with alpha 0 ==> color key -returns 2 if the palette is semi-translucent. -*/ -static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) { - size_t i; - unsigned key = 0; - unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ - for(i = 0; i != palettesize; ++i) { - if(!key && palette[4 * i + 3] == 0) { - r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; - key = 1; - i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ - } - else if(palette[4 * i + 3] != 255) return 2; - /*when key, no opaque RGB may have key's RGB*/ - else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; - } - return key; -} - #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { unsigned char* inchunk = data; @@ -5817,11 +5816,10 @@ LodePNGState* state) { unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ size_t datasize = 0; - ucvector outv; + ucvector outv = ucvector_init(NULL, 0); LodePNGInfo info; const LodePNGInfo* info_png = &state->info_png; - ucvector_init(&outv); lodepng_info_init(&info); /*provide some proper output values if error will happen*/ @@ -5836,17 +5834,17 @@ goto cleanup; } if(state->encoder.zlibsettings.btype > 2) { - state->error = 61; /*error: nonexistent btype*/ + state->error = 61; /*error: invalid btype*/ goto cleanup; } if(info_png->interlace_method > 1) { - state->error = 71; /*error: nonexistent interlace mode*/ + state->error = 71; /*error: invalid interlace mode*/ goto cleanup; } state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth); - if(state->error) goto cleanup; /*error: nonexistent color type given*/ + if(state->error) goto cleanup; /*error: invalid color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); - if(state->error) goto cleanup; /*error: nonexistent color type given*/ + if(state->error) goto cleanup; /*error: invalid color type given*/ /* color convert and compute scanline filter types */ lodepng_info_copy(&info, &state->info_png); @@ -5917,20 +5915,26 @@ if(!state->error) { state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } - if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + if(!state->error) { + state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + } lodepng_free(converted); if(state->error) goto cleanup; + } else { + state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + if(state->error) goto cleanup; } - else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); /* output all PNG chunks */ { #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS size_t i; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*write signature and chunks*/ - writeSignature(&outv); + state->error = writeSignature(&outv); + if(state->error) goto cleanup; /*IHDR*/ - addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); + state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); + if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*unknown chunks between IHDR and PLTE*/ if(info.unknown_chunks_data[0]) { @@ -5938,25 +5942,36 @@ if(state->error) goto cleanup; } /*color profile chunks must come before PLTE */ - if(info.iccp_defined) addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); - if(info.srgb_defined) addChunk_sRGB(&outv, &info); - if(info.gama_defined) addChunk_gAMA(&outv, &info); - if(info.chrm_defined) addChunk_cHRM(&outv, &info); + if(info.iccp_defined) { + state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } + if(info.srgb_defined) { + state->error = addChunk_sRGB(&outv, &info); + if(state->error) goto cleanup; + } + if(info.gama_defined) { + state->error = addChunk_gAMA(&outv, &info); + if(state->error) goto cleanup; + } + if(info.chrm_defined) { + state->error = addChunk_cHRM(&outv, &info); + if(state->error) goto cleanup; + } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) { - addChunk_PLTE(&outv, &info.color); + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; } if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { - addChunk_PLTE(&outv, &info.color); - } - /*tRNS*/ - if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) { - addChunk_tRNS(&outv, &info.color); - } - if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) { - addChunk_tRNS(&outv, &info.color); + /*force_palette means: write suggested palette for truecolor in PLTE chunk*/ + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; } + /*tRNS (this will only add if when necessary) */ + state->error = addChunk_tRNS(&outv, &info.color); + if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*bKGD (must come between PLTE and the IDAt chunks*/ if(info.background_defined) { @@ -5964,7 +5979,10 @@ if(state->error) goto cleanup; } /*pHYs (must come before the IDAT chunks)*/ - if(info.phys_defined) addChunk_pHYs(&outv, &info); + if(info.phys_defined) { + state->error = addChunk_pHYs(&outv, &info); + if(state->error) goto cleanup; + } /*unknown chunks between PLTE and IDAT*/ if(info.unknown_chunks_data[1]) { @@ -5977,7 +5995,10 @@ if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*tIME*/ - if(info.time_defined) addChunk_tIME(&outv, &info.time); + if(info.time_defined) { + state->error = addChunk_tIME(&outv, &info.time); + if(state->error) goto cleanup; + } /*tEXt and/or zTXt*/ for(i = 0; i != info.text_num; ++i) { if(lodepng_strlen(info.text_keys[i]) > 79) { @@ -5989,9 +6010,11 @@ goto cleanup; } if(state->encoder.text_compression) { - addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + if(state->error) goto cleanup; } else { - addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + if(state->error) goto cleanup; } } /*LodePNG version id in text chunk*/ @@ -6007,7 +6030,8 @@ } } if(already_added_id_text == 0) { - addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + if(state->error) goto cleanup; } } /*iTXt*/ @@ -6020,9 +6044,11 @@ state->error = 67; /*text chunk too small*/ goto cleanup; } - addChunk_iTXt(&outv, state->encoder.text_compression, - info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], - &state->encoder.zlibsettings); + state->error = addChunk_iTXt( + &outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + if(state->error) goto cleanup; } /*unknown chunks between IDAT and IEND*/ @@ -6031,7 +6057,8 @@ if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ - addChunk_IEND(&outv); + state->error = addChunk_IEND(&outv); + if(state->error) goto cleanup; } cleanup: @@ -6119,7 +6146,7 @@ case 14: return "problem while processing dynamic deflate block"; case 15: return "problem while processing dynamic deflate block"; /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/ - case 16: return "nonexistent code while processing dynamic deflate block"; + case 16: return "invalid code while processing dynamic deflate block"; case 17: return "end of out buffer memory reached while inflating"; case 18: return "invalid distance code while inflating"; case 19: return "end of out buffer memory reached while inflating"; @@ -6182,8 +6209,8 @@ case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; - case 71: return "nonexistent interlace mode given to encoder (must be 0 or 1)"; - case 72: return "while decoding, nonexistent compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 71: return "invalid interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)"; case 73: return "invalid tIME chunk size"; case 74: return "invalid pHYs chunk size"; /*length could be wrong, or data chopped off*/ @@ -6221,6 +6248,16 @@ case 106: return "PNG file must have PLTE chunk if color type is palette"; case 107: return "color convert from palette mode requested without setting the palette data in it"; case 108: return "tried to add more than 256 values to a palette"; + /*this limit can be configured in LodePNGDecompressSettings*/ + case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size"; + case 110: return "custom zlib or inflate decompression failed"; + case 111: return "custom zlib or deflate compression failed"; + /*max text size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large text sizes.*/ + case 112: return "compressed text unreasonably large"; + /*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large ICC profile*/ + case 113: return "ICC profile unreasonably large"; } return "unknown error code"; } @@ -6255,7 +6292,7 @@ const LodePNGDecompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; - unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); + unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); @@ -6314,7 +6351,7 @@ unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { - unsigned char* buffer; + unsigned char* buffer = 0; unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); if(buffer && !error) { State state; @@ -6322,8 +6359,8 @@ state.info_raw.bitdepth = bitdepth; size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); - lodepng_free(buffer); } + lodepng_free(buffer); return error; } diff -Nru refind-0.12.0/libeg/lodepng.h refind-0.13.2/libeg/lodepng.h --- refind-0.12.0/libeg/lodepng.h 2020-02-13 03:28:13.000000000 +0000 +++ refind-0.13.2/libeg/lodepng.h 2021-02-27 04:01:39.000000000 +0000 @@ -1,5 +1,5 @@ /* -LodePNG version 20200211 +LodePNG version 20201017 Copyright (c) 2005-2020 Lode Vandevenne @@ -28,7 +28,7 @@ * for GNU-EFI compatibility. The associated lodepng.c file is unmodified * from the original. */ - + #ifndef LODEPNG_H #define LODEPNG_H @@ -293,12 +293,21 @@ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ - /*use custom zlib decoder instead of built in one (default: null)*/ + /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding, + return an error, output a data size > max_output_size and all the data up to that point. This is + not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is + ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones. + Set to 0 to impose no limit (the default).*/ + size_t max_output_size; + + /*use custom zlib decoder instead of built in one (default: null). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); /*use custom deflate decoder instead of built in one (default: null) - if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/ + if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); @@ -477,30 +486,36 @@ unsigned background_b; /*blue component of suggested background color*/ /* - non-international text chunks (tEXt and zTXt) + Non-international text chunks (tEXt and zTXt) The char** arrays each contain num strings. The actual messages are in text_strings, while text_keys are keywords that give a short description what the actual text represents, e.g. Title, Author, Description, or anything else. - All the string fields below including keys, names and language tags are null terminated. + All the string fields below including strings, keys, names and language tags are null terminated. The PNG specification uses null characters for the keys, names and tags, and forbids null characters to appear in the main text which is why we can use null termination everywhere here. - A keyword is minimum 1 character and maximum 79 characters long. It's - discouraged to use a single line length longer than 79 characters for texts. + A keyword is minimum 1 character and maximum 79 characters long (plus the + additional null terminator). It's discouraged to use a single line length + longer than 79 characters for texts. Don't allocate these text buffers yourself. Use the init/cleanup functions correctly and use lodepng_add_text and lodepng_clear_text. + + Standard text chunk keywords and strings are encoded using Latin-1. */ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ char** text_strings; /*the actual text*/ /* - international text chunks (iTXt) + International text chunks (iTXt) Similar to the non-international text chunks, but with additional strings - "langtags" and "transkeys". + "langtags" and "transkeys", and the following text encodings are used: + keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8. + keys must be 1-79 characters (plus the additional null terminator), the other + strings are any length. */ size_t itext_num; /*the amount of international texts in this PNG*/ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ @@ -662,8 +677,19 @@ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ unsigned remember_unknown_chunks; + + /* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned, + unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size. + By default it is a value that prevents unreasonably large strings from hogging memory. */ + size_t max_text_size; + + /* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to + 0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any + legitimate profile could be to hog memory. */ + size_t max_icc_size; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGDecoderSettings; @@ -894,18 +920,18 @@ /* Appends chunk to the data in out. The given chunk should already have its chunk header. -The out variable and outlength are updated to reflect the new reallocated buffer. +The out variable and outsize are updated to reflect the new reallocated buffer. Returns error code (0 if it went ok) */ -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk); /* Appends new chunk to out. The chunk to append is given by giving its length, type and data separately. The type is a 4-letter string. -The out variable and outlength are updated to reflect the new reallocated buffer. +The out variable and outsize are updated to reflect the new reallocated buffer. Returne error code (0 if it went ok) */ -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, const char* type, const unsigned char* data); @@ -1077,8 +1103,7 @@ [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes [ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... [ ] allow user to give data (void*) to custom allocator -[ ] provide alternatives for C library functions not present on some platforms (memcpy, ...) -[ ] rename "grey" to "gray" everywhere since "color" also uses US spelling (keep "grey" copies for backwards compatibility) +[X] provide alternatives for C library functions not present on some platforms (memcpy, ...) */ #endif /*LODEPNG_H inclusion guard*/ @@ -1529,6 +1554,11 @@ Check the implementation of lodepng_error_text to see the meaning of each code. +It is not recommended to use the numerical values to programmatically make +different decisions based on error types as the numbers are not guaranteed to +stay backwards compatible. They are for human consumption only. Programmatically +only 0 or non-0 matter. + 8. chunks and PNG editing ------------------------- @@ -1594,12 +1624,12 @@ functions do no boundary checking of the allocated data whatsoever, so make sure there is enough data available in the buffer to be able to go to the next chunk. -unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): -unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, const char* type, const unsigned char* data): These functions are used to create new chunks that are appended to the data in *out that has -length *outlength. The append function appends an existing chunk to the new data. The create +length *outsize. The append function appends an existing chunk to the new data. The create function creates a new chunk with the given parameters and appends it. Type is the 4-letter name of the chunk. @@ -1799,6 +1829,8 @@ Not all changes are listed here, the commit history in github lists more: https://github.com/lvandeve/lodepng +*) 17 okt 2020: prevent decoding too large text/icc chunks by default. +*) 06 mar 2020: simplified some of the dynamic memory allocations. *) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct overflow checks. *) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables. diff -Nru refind-0.12.0/libeg/lodepng_xtra.c refind-0.13.2/libeg/lodepng_xtra.c --- refind-0.12.0/libeg/lodepng_xtra.c 2018-07-16 13:44:39.000000000 +0000 +++ refind-0.13.2/libeg/lodepng_xtra.c 2021-03-12 15:22:41.000000000 +0000 @@ -118,8 +118,13 @@ // allocate image structure and buffer NewImage = egCreateImage(Width, Height, WantAlpha); - if ((NewImage == NULL) || (NewImage->Width != Width) || (NewImage->Height != Height)) + if (NewImage == NULL) + return NULL; + // The following really should never happen; just being paranoid.... + if ((NewImage->Width != Width) || (NewImage->Height != Height)) { + egFreeImage(NewImage); return NULL; + } LodeData = (lode_color *) PixelData; diff -Nru refind-0.12.0/libeg/screen.c refind-0.13.2/libeg/screen.c --- refind-0.12.0/libeg/screen.c 2020-02-19 18:28:05.000000000 +0000 +++ refind-0.13.2/libeg/screen.c 2021-03-13 18:04:30.000000000 +0000 @@ -61,6 +61,7 @@ #include "../refind/mystrings.h" #include "../include/refit_call_wrapper.h" #include "libeg.h" +#include "log.h" #include "../include/Handle.h" #include @@ -68,6 +69,8 @@ #ifndef __MAKEWITH_GNUEFI #define LibLocateProtocol EfiLibLocateProtocol +#else +#include #endif // Console defines and variables @@ -81,14 +84,81 @@ static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; -static BOOLEAN egHasGraphics = FALSE; -static UINTN egScreenWidth = 800; -static UINTN egScreenHeight = 600; +static BOOLEAN egHasGraphics = FALSE; +static UINTN egScreenWidth = 800; +static UINTN egScreenHeight = 600; + // // Screen handling // +// On GOP systems, set the maximum available resolution. +// On UGA systems, just record the current resolution. +VOID egSetMaxResolution(VOID) { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + + EFI_STATUS Status = EFI_UNSUPPORTED; + UINTN Zero = 0; + UINT32 Width = 0; + UINT32 Height = 0; + UINT32 BestMode = 0; + UINT32 Mode; + UINT32 UGAWidth, UGAHeight, UGADepth, UGARefreshRate; + UINTN SizeOfInfo; + BOOLEAN Result; + + if (GraphicsOutput == NULL) { + // Can't do this in UGA or text mode, so get and set basic data and then + // quietly ignore.... + Status = refit_call5_wrapper(UgaDraw->GetMode, + UgaDraw, + &UGAWidth, + &UGAHeight, + &UGADepth, + &UGARefreshRate); + egScreenWidth = GlobalConfig.RequestedScreenWidth = UGAWidth; + egScreenHeight = GlobalConfig.RequestedScreenHeight = UGAHeight; + return; + } + + for (Mode = 0; Mode < GraphicsOutput->Mode->MaxMode; Mode++) { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, + GraphicsOutput, + Mode, + &SizeOfInfo, + &Info); + if (!EFI_ERROR(Status)) { + if ((Width < Info->HorizontalResolution) && (Height < Info->VerticalResolution)) { + Width = Info->HorizontalResolution; + Height = Info->VerticalResolution; + BestMode = Mode; + } + } // if() + } // for() + + // Check if requested mode is equal to current mode; if so, record the + // fact and move on.... + if (BestMode == GraphicsOutput->Mode->Mode) { + egScreenWidth = GlobalConfig.RequestedScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution; + egScreenHeight = GlobalConfig.RequestedScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution; + Status = EFI_SUCCESS; + } else { // Need to set the new mode.... + Result = egSetScreenSize((UINTN*) &BestMode, &Zero); + if (Result) { + egScreenWidth = GlobalConfig.RequestedScreenWidth = Width; + egScreenHeight = GlobalConfig.RequestedScreenHeight = Height; + } else { + // we can not set BestMode, so use the current mode + BestMode = GraphicsOutput->Mode->Mode; + Zero = 0; + Result = egSetScreenSize((UINTN*) &BestMode, &Zero); + } // if/else + } // if/else + + return; +} // VOID egSetMaxResolution() + // Make the necessary system calls to identify the current graphics mode. // Stores the results in the file-global variables egScreenWidth, // egScreenHeight, and egHasGraphics. The first two of these will be @@ -108,7 +178,7 @@ if (EFI_ERROR(Status)) { UgaDraw = NULL; // graphics not available } else { - egScreenWidth = UGAWidth; + egScreenWidth = UGAWidth; egScreenHeight = UGAHeight; egHasGraphics = TRUE; } @@ -123,7 +193,7 @@ *ScreenWidth = egScreenWidth; if (ScreenHeight != NULL) *ScreenHeight = egScreenHeight; -} +} // VOID egGetScreenSize() VOID egInitScreen(VOID) { @@ -142,26 +212,31 @@ if (EFI_ERROR(Status)) GraphicsOutput = NULL; + if ((GlobalConfig.RequestedScreenHeight == MAX_RES_CODE) && + (GlobalConfig.RequestedScreenWidth == MAX_RES_CODE)) { + egSetMaxResolution(); + } egDetermineScreenSize(); -} +} // VOID egInitScreen() // Convert a graphics mode (in *ModeWidth) to a width and height (returned in // *ModeWidth and *Height, respectively). // Returns TRUE if successful, FALSE if not (invalid mode, typically) BOOLEAN egGetResFromMode(UINTN *ModeWidth, UINTN *Height) { - UINTN Size; - EFI_STATUS Status; - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = NULL; - - if ((ModeWidth != NULL) && (Height != NULL)) { - Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, *ModeWidth, &Size, &Info); - if ((Status == EFI_SUCCESS) && (Info != NULL)) { - *ModeWidth = Info->HorizontalResolution; - *Height = Info->VerticalResolution; - return TRUE; - } - } - return FALSE; + UINTN Size; + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = NULL; + + if ((ModeWidth != NULL) && (Height != NULL) && GraphicsOutput) { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, + *ModeWidth, &Size, &Info); + if (!EFI_ERROR(Status) && (Info != NULL)) { + *ModeWidth = Info->HorizontalResolution; + *Height = Info->VerticalResolution; + return TRUE; + } + } + return FALSE; } // BOOLEAN egGetResFromMode() // Sets the screen resolution to the specified value, if possible. If *ScreenHeight @@ -169,85 +244,110 @@ // number rather than a horizontal resolution. If the specified resolution is not // valid, displays a warning with the valid modes on GOP (UEFI) systems, or silently // fails on UGA (EFI 1.x) systems. Note that this function attempts to set ANY screen -// resolution, even 0x0 or ridiculously large values. +// resolution, even 1x1 or ridiculously large values. // Upon success, returns actual screen resolution in *ScreenWidth and *ScreenHeight. // These values are unchanged upon failure. // Returns TRUE if successful, FALSE if not. BOOLEAN egSetScreenSize(IN OUT UINTN *ScreenWidth, IN OUT UINTN *ScreenHeight) { - EFI_STATUS Status = EFI_SUCCESS; - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; - UINTN Size; - UINT32 ModeNum = 0, CurrentModeNum; - UINT32 UGAWidth, UGAHeight, UGADepth, UGARefreshRate; - BOOLEAN ModeSet = FALSE; - - if ((ScreenWidth == NULL) || (ScreenHeight == NULL)) - return FALSE; - - if (GraphicsOutput != NULL) { // GOP mode (UEFI) - CurrentModeNum = GraphicsOutput->Mode->Mode; - if (*ScreenHeight == 0) { // User specified a mode number (stored in *ScreenWidth); use it directly - ModeNum = (UINT32) *ScreenWidth; - if (ModeNum != CurrentModeNum) { - ModeSet = TRUE; - } else if (egGetResFromMode(ScreenWidth, ScreenHeight) && - (refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum) == EFI_SUCCESS)) { + EFI_STATUS Status = EFI_SUCCESS; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + UINTN Size; + UINT32 ModeNum = 0, CurrentModeNum; + UINT32 UGAWidth, UGAHeight, UGADepth, UGARefreshRate; + BOOLEAN ModeSet = FALSE; + + if ((ScreenWidth == NULL) || (ScreenHeight == NULL)) { + LOG(1, LOG_LINE_NORMAL, L"Error: ScreenWidth or ScreenHeight is NULL in egSetScreenSize()!"); + return FALSE; + } + + if (GraphicsOutput != NULL) { // GOP mode (UEFI) + CurrentModeNum = GraphicsOutput->Mode->Mode; + + if (*ScreenHeight == 0) { // User specified a mode number (stored in *ScreenWidth); use it directly + ModeNum = (UINT32) *ScreenWidth; + if (ModeNum == CurrentModeNum) { + ModeSet = TRUE; + } else if (egGetResFromMode(ScreenWidth, ScreenHeight) && + (refit_call2_wrapper(GraphicsOutput->SetMode, + GraphicsOutput, ModeNum) == EFI_SUCCESS)) { + LOG(2, LOG_LINE_NORMAL, L"Setting GOP mode to %d", ModeNum); + ModeSet = TRUE; + } + + // User specified width & height; must find mode... + } else { + // Do a loop through the modes to see if the specified one is available; + // and if so, switch to it.... + do { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, + ModeNum, &Size, &Info); + if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info) && (Info != NULL)) && + (Info->HorizontalResolution == *ScreenWidth) && + (Info->VerticalResolution == *ScreenHeight) && + ((ModeNum == CurrentModeNum) || + (refit_call2_wrapper(GraphicsOutput->SetMode, + GraphicsOutput, ModeNum) == EFI_SUCCESS))) { + LOG(2, LOG_LINE_NORMAL, L"Setting GOP mode to %d (%dx%d)", + ModeNum, *ScreenWidth, *ScreenHeight); + ModeSet = TRUE; + } // if + } while ((++ModeNum < GraphicsOutput->Mode->MaxMode) && !ModeSet); + } // if/else + + if (ModeSet) { + egScreenWidth = *ScreenWidth; + egScreenHeight = *ScreenHeight; + } else {// If unsuccessful, display an error message for the user.... + SwitchToText(FALSE); + Print(L"Error setting graphics mode %d x %d; using default mode!\nAvailable modes are:\n", + *ScreenWidth, *ScreenHeight); + LOG(1, LOG_LINE_NORMAL, L"Error setting graphics mode %d x %d; using default mode!", + *ScreenWidth, *ScreenHeight) + LOG(1, LOG_LINE_NORMAL, L"Available modes are:"); + ModeNum = 0; + do { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, + GraphicsOutput, ModeNum, &Size, &Info); + if (!EFI_ERROR(Status) && (Info != NULL)) { + Print(L"Mode %d: %d x %d\n", ModeNum, + Info->HorizontalResolution, Info->VerticalResolution); + LOG(1, LOG_LINE_NORMAL, L" Mode %d: %d x %d", ModeNum, + Info->HorizontalResolution, Info->VerticalResolution); + if (ModeNum == CurrentModeNum) { + egScreenWidth = Info->HorizontalResolution; + egScreenHeight = Info->VerticalResolution; + } // if + } // else + } while (++ModeNum < GraphicsOutput->Mode->MaxMode); + PauseForKey(); + SwitchToGraphics(); + } // if GOP mode (UEFI) + + } else if ((UgaDraw != NULL) && (*ScreenHeight > 0)) { // UGA mode (EFI 1.x) + // Try to use current color depth & refresh rate for new mode. Maybe not the best choice + // in all cases, but I don't know how to probe for alternatives.... + Status = refit_call5_wrapper(UgaDraw->GetMode, UgaDraw, &UGAWidth, + &UGAHeight, &UGADepth, &UGARefreshRate); + LOG(1, LOG_LINE_NORMAL, L"Setting UGA Draw mode to %d x %d", *ScreenWidth, *ScreenHeight); + Status = refit_call5_wrapper(UgaDraw->SetMode, UgaDraw, *ScreenWidth, + *ScreenHeight, UGADepth, UGARefreshRate); + if (!EFI_ERROR(Status)) { + egScreenWidth = *ScreenWidth; + egScreenHeight = *ScreenHeight; ModeSet = TRUE; - } + } else { + // TODO: Find a list of supported modes and display it. + // NOTE: Below doesn't actually appear unless we explicitly switch to text mode. + // This is just a placeholder until something better can be done.... + Print(L"Error setting graphics mode %d x %d; unsupported mode!\n", + *ScreenWidth, *ScreenHeight); + LOG(1, LOG_LINE_NORMAL, L"Error setting graphics mode %d x %d; unsupported mode!", + *ScreenWidth, *ScreenHeight); + } // if/else + } // if/else if (UgaDraw != NULL) - // User specified width & height; must find mode... - } else { - // Do a loop through the modes to see if the specified one is available; - // and if so, switch to it.... - do { - Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); - if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info) && (Info != NULL)) && - (Info->HorizontalResolution == *ScreenWidth) && (Info->VerticalResolution == *ScreenHeight) && - ((ModeNum == CurrentModeNum) || - (refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum) == EFI_SUCCESS))) { - ModeSet = TRUE; - } // if - } while ((++ModeNum < GraphicsOutput->Mode->MaxMode) && !ModeSet); - } // if/else - - if (ModeSet) { - egScreenWidth = *ScreenWidth; - egScreenHeight = *ScreenHeight; - } else {// If unsuccessful, display an error message for the user.... - SwitchToText(FALSE); - Print(L"Error setting graphics mode %d x %d; using default mode!\nAvailable modes are:\n", *ScreenWidth, *ScreenHeight); - ModeNum = 0; - do { - Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); - if ((Status == EFI_SUCCESS) && (Info != NULL)) { - Print(L"Mode %d: %d x %d\n", ModeNum, Info->HorizontalResolution, Info->VerticalResolution); - if (ModeNum == CurrentModeNum) { - egScreenWidth = Info->HorizontalResolution; - egScreenHeight = Info->VerticalResolution; - } // if - } // else - } while (++ModeNum < GraphicsOutput->Mode->MaxMode); - PauseForKey(); - SwitchToGraphics(); - } // if GOP mode (UEFI) - - } else if (UgaDraw != NULL) { // UGA mode (EFI 1.x) - // Try to use current color depth & refresh rate for new mode. Maybe not the best choice - // in all cases, but I don't know how to probe for alternatives.... - Status = refit_call5_wrapper(UgaDraw->GetMode, UgaDraw, &UGAWidth, &UGAHeight, &UGADepth, &UGARefreshRate); - Status = refit_call5_wrapper(UgaDraw->SetMode, UgaDraw, *ScreenWidth, *ScreenHeight, UGADepth, UGARefreshRate); - if (Status == EFI_SUCCESS) { - egScreenWidth = *ScreenWidth; - egScreenHeight = *ScreenHeight; - ModeSet = TRUE; - } else { - // TODO: Find a list of supported modes and display it. - // NOTE: Below doesn't actually appear unless we explicitly switch to text mode. - // This is just a placeholder until something better can be done.... - Print(L"Error setting graphics mode %d x %d; unsupported mode!\n"); - } // if/else - } // if/else if UGA mode (EFI 1.x) - return (ModeSet); + return (ModeSet); } // BOOLEAN egSetScreenSize() // Set a text mode. @@ -255,59 +355,57 @@ // Note that a FALSE return value can mean either an error or no change // necessary. BOOLEAN egSetTextMode(UINT32 RequestedMode) { - UINTN i = 0, Width, Height; - EFI_STATUS Status; - BOOLEAN ChangedIt = FALSE; - - if ((RequestedMode != DONT_CHANGE_TEXT_MODE) && (RequestedMode != ST->ConOut->Mode->Mode)) { - Status = refit_call2_wrapper(ST->ConOut->SetMode, ST->ConOut, RequestedMode); - if (Status == EFI_SUCCESS) { - ChangedIt = TRUE; - } else { - SwitchToText(FALSE); - Print(L"\nError setting text mode %d; available modes are:\n", RequestedMode); - do { - Status = refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, i, &Width, &Height); - if (Status == EFI_SUCCESS) - Print(L"Mode %d: %d x %d\n", i, Width, Height); - } while (++i < ST->ConOut->Mode->MaxMode); - Print(L"Mode %d: Use default mode\n", DONT_CHANGE_TEXT_MODE); - - PauseForKey(); - SwitchToGraphics(); - } // if/else successful change - } // if need to change mode - return ChangedIt; + UINTN i = 0, Width, Height; + EFI_STATUS Status; + BOOLEAN ChangedIt = FALSE; + + if ((RequestedMode != DONT_CHANGE_TEXT_MODE) && (RequestedMode != ST->ConOut->Mode->Mode)) { + LOG(1, LOG_LINE_NORMAL, L"Setting text mode to %d", RequestedMode); + Status = refit_call2_wrapper(ST->ConOut->SetMode, ST->ConOut, RequestedMode); + if (Status == EFI_SUCCESS) { + ChangedIt = TRUE; + } else { + SwitchToText(FALSE); + Print(L"\nError setting text mode %d; available modes are:\n", RequestedMode); + LOG(1, LOG_LINE_NORMAL, L"Error setting text mode %d; available modes are:", RequestedMode); + do { + Status = refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, i, &Width, &Height); + if (Status == EFI_SUCCESS) { + Print(L"Mode %d: %d x %d\n", i, Width, Height); + LOG(1, LOG_LINE_NORMAL, L" Mode %d: %d x %d", i, Width, Height); + } + } while (++i < ST->ConOut->Mode->MaxMode); + Print(L"Mode %d: Use default mode\n", DONT_CHANGE_TEXT_MODE); + LOG(1, LOG_LINE_NORMAL, L" Mode %d: Use default mode", DONT_CHANGE_TEXT_MODE); + + PauseForKey(); + SwitchToGraphics(); + } // if/else successful change + } // if need to change mode + return ChangedIt; } // BOOLEAN egSetTextMode() CHAR16 * egScreenDescription(VOID) { - CHAR16 *GraphicsInfo, *TextInfo = NULL; - - GraphicsInfo = AllocateZeroPool(256 * sizeof(CHAR16)); - if (GraphicsInfo == NULL) - return L"memory allocation error"; + CHAR16 *GraphicsInfo = NULL, *TextInfo; if (egHasGraphics) { if (GraphicsOutput != NULL) { - SPrint(GraphicsInfo, 255, L"Graphics Output (UEFI), %dx%d", egScreenWidth, egScreenHeight); + GraphicsInfo = PoolPrint(L"Graphics Output (UEFI), %dx%d", egScreenWidth, egScreenHeight); } else if (UgaDraw != NULL) { - GraphicsInfo = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(GraphicsInfo, 255, L"UGA Draw (EFI 1.10), %dx%d", egScreenWidth, egScreenHeight); + GraphicsInfo = PoolPrint(L"UGA Draw (EFI 1.10), %dx%d", egScreenWidth, egScreenHeight); } else { MyFreePool(GraphicsInfo); - MyFreePool(TextInfo); - return L"Internal Error"; + return StrDuplicate(L"Internal Error"); } if (!AllowGraphicsMode) { // graphics-capable HW, but in text mode - TextInfo = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(TextInfo, 255, L"(in %dx%d text mode)", ConWidth, ConHeight); - MergeStrings(&GraphicsInfo, TextInfo, L' '); + TextInfo = PoolPrint(L"(in %dx%d text mode)", ConWidth, ConHeight); + MergeStrings(&GraphicsInfo, TextInfo, L' '); + MyFreePool(TextInfo); } } else { - SPrint(GraphicsInfo, 255, L"Text-foo console, %dx%d", ConWidth, ConHeight); + GraphicsInfo = PoolPrint(L"Text-only console, %dx%d", ConWidth, ConHeight); } - MyFreePool(TextInfo); return GraphicsInfo; } @@ -339,7 +437,7 @@ NewMode = Enable ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText; if (CurrentMode != NewMode) - refit_call2_wrapper(ConsoleControl->SetMode, ConsoleControl, NewMode); + refit_call2_wrapper(ConsoleControl->SetMode, ConsoleControl, NewMode); } } @@ -355,13 +453,13 @@ return; if (Color != NULL) { - FillColor.Red = Color->r; - FillColor.Green = Color->g; - FillColor.Blue = Color->b; + FillColor.Red = Color->r; + FillColor.Green = Color->g; + FillColor.Blue = Color->b; } else { - FillColor.Red = 0x0; - FillColor.Green = 0x0; - FillColor.Blue = 0x0; + FillColor.Red = 0x0; + FillColor.Green = 0x0; + FillColor.Blue = 0x0; } FillColor.Reserved = 0; @@ -383,16 +481,19 @@ // NOTE: Weird seemingly redundant tests because some placement code can "wrap around" and // send "negative" values, which of course become very large unsigned ints that can then // wrap around AGAIN if values are added to them..... - if (!egHasGraphics || ((ScreenPosX + Image->Width) > egScreenWidth) || ((ScreenPosY + Image->Height) > egScreenHeight) || + if (!egHasGraphics || ((ScreenPosX + Image->Width) > egScreenWidth) || + ((ScreenPosY + Image->Height) > egScreenHeight) || (ScreenPosX > egScreenWidth) || (ScreenPosY > egScreenHeight)) - return; + return; - if ((GlobalConfig.ScreenBackground == NULL) || ((Image->Width == egScreenWidth) && (Image->Height == egScreenHeight))) { + if ((GlobalConfig.ScreenBackground == NULL) || ((Image->Width == egScreenWidth) && + (Image->Height == egScreenHeight))) { CompImage = Image; } else if (GlobalConfig.ScreenBackground == Image) { CompImage = GlobalConfig.ScreenBackground; } else { - CompImage = egCropImage(GlobalConfig.ScreenBackground, ScreenPosX, ScreenPosY, Image->Width, Image->Height); + CompImage = egCropImage(GlobalConfig.ScreenBackground, ScreenPosX, ScreenPosY, + Image->Width, Image->Height); if (CompImage == NULL) { Print(L"Error! Can't crop image in egDrawImage()!\n"); return; @@ -401,11 +502,14 @@ } if (GraphicsOutput != NULL) { - refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)CompImage->PixelData, - EfiBltBufferToVideo, 0, 0, ScreenPosX, ScreenPosY, CompImage->Width, CompImage->Height, 0); + refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)CompImage->PixelData, + EfiBltBufferToVideo, 0, 0, ScreenPosX, ScreenPosY, CompImage->Width, + CompImage->Height, 0); } else if (UgaDraw != NULL) { - refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)CompImage->PixelData, EfiUgaBltBufferToVideo, - 0, 0, ScreenPosX, ScreenPosY, CompImage->Width, CompImage->Height, 0); + refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)CompImage->PixelData, + EfiUgaBltBufferToVideo, 0, 0, ScreenPosX, ScreenPosY, + CompImage->Width, CompImage->Height, 0); } if ((CompImage != GlobalConfig.ScreenBackground) && (CompImage != Image)) egFreeImage(CompImage); @@ -414,14 +518,15 @@ // Display an unselected icon on the screen, so that the background image shows // through the transparency areas. The BadgeImage may be NULL, in which case // it's not composited in. -VOID egDrawImageWithTransparency(EG_IMAGE *Image, EG_IMAGE *BadgeImage, UINTN XPos, UINTN YPos, UINTN Width, UINTN Height) { - EG_IMAGE *Background; - - Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, Width, Height); - if (Background != NULL) { - BltImageCompositeBadge(Background, Image, BadgeImage, XPos, YPos); - egFreeImage(Background); - } +VOID egDrawImageWithTransparency(EG_IMAGE *Image, EG_IMAGE *BadgeImage, + UINTN XPos, UINTN YPos, UINTN Width, UINTN Height) { + EG_IMAGE *Background; + + Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, Width, Height); + if (Background != NULL) { + BltImageCompositeBadge(Background, Image, BadgeImage, XPos, YPos); + egFreeImage(Background); + } } // VOID DrawImageWithTransparency() VOID egDrawImageArea(IN EG_IMAGE *Image, @@ -450,67 +555,70 @@ // specified color. For the moment, uses graphics calls only. (It still works // in text mode on GOP/UEFI systems, but not on UGA/EFI 1.x systems.) VOID egDisplayMessage(IN CHAR16 *Text, EG_PIXEL *BGColor, UINTN PositionCode) { - UINTN BoxWidth, BoxHeight; - static UINTN Position = 1; - EG_IMAGE *Box; - - if ((Text != NULL) && (BGColor != NULL)) { - egMeasureText(Text, &BoxWidth, &BoxHeight); - BoxWidth += 14; - BoxHeight *= 2; - if (BoxWidth > egScreenWidth) - BoxWidth = egScreenWidth; - Box = egCreateFilledImage(BoxWidth, BoxHeight, FALSE, BGColor); - egRenderText(Text, Box, 7, BoxHeight / 4, (BGColor->r + BGColor->g + BGColor->b) / 3); - switch (PositionCode) { - case CENTER: - Position = (egScreenHeight - BoxHeight) / 2; - break; - case BOTTOM: - Position = egScreenHeight - (BoxHeight * 2); - break; - case TOP: - Position = 1; - break; - default: // NEXTLINE - Position += BoxHeight + (BoxHeight / 10); - break; - } // switch() - egDrawImage(Box, (egScreenWidth - BoxWidth) / 2, Position); - if ((PositionCode == CENTER) || (Position >= egScreenHeight - (BoxHeight * 5))) - Position = 1; - } // if non-NULL inputs + UINTN BoxWidth, BoxHeight; + static UINTN Position = 1; + EG_IMAGE *Box; + + if ((Text != NULL) && (BGColor != NULL)) { + egMeasureText(Text, &BoxWidth, &BoxHeight); + BoxWidth += 14; + BoxHeight *= 2; + if (BoxWidth > egScreenWidth) + BoxWidth = egScreenWidth; + Box = egCreateFilledImage(BoxWidth, BoxHeight, FALSE, BGColor); + egRenderText(Text, Box, 7, BoxHeight / 4, (BGColor->r + BGColor->g + BGColor->b) / 3); + switch (PositionCode) { + case CENTER: + Position = (egScreenHeight - BoxHeight) / 2; + break; + case BOTTOM: + Position = egScreenHeight - (BoxHeight * 2); + break; + case TOP: + Position = 1; + break; + default: // NEXTLINE + Position += BoxHeight + (BoxHeight / 10); + break; + } // switch() + egDrawImage(Box, (egScreenWidth - BoxWidth) / 2, Position); + if ((PositionCode == CENTER) || (Position >= egScreenHeight - (BoxHeight * 5))) + Position = 1; + } // if non-NULL inputs } // VOID egDisplayMessage() // Copy the current contents of the display into an EG_IMAGE.... // Returns pointer if successful, NULL if not. EG_IMAGE * egCopyScreen(VOID) { - return egCopyScreenArea(0, 0, egScreenWidth, egScreenHeight); + return egCopyScreenArea(0, 0, egScreenWidth, egScreenHeight); } // EG_IMAGE * egCopyScreen() // Copy the current contents of the specified display area into an EG_IMAGE.... // Returns pointer if successful, NULL if not. EG_IMAGE * egCopyScreenArea(UINTN XPos, UINTN YPos, UINTN Width, UINTN Height) { - EG_IMAGE *Image = NULL; + EG_IMAGE *Image = NULL; - if (!egHasGraphics) - return NULL; + if (!egHasGraphics) + return NULL; + + // allocate a buffer for the screen area + Image = egCreateImage(Width, Height, FALSE); + if (Image == NULL) { + return NULL; + } - // allocate a buffer for the screen area - Image = egCreateImage(Width, Height, FALSE); - if (Image == NULL) { - return NULL; - } - - // get full screen image - if (GraphicsOutput != NULL) { - refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)Image->PixelData, - EfiBltVideoToBltBuffer, XPos, YPos, 0, 0, Image->Width, Image->Height, 0); - } else if (UgaDraw != NULL) { - refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)Image->PixelData, EfiUgaVideoToBltBuffer, - XPos, YPos, 0, 0, Image->Width, Image->Height, 0); - } - return Image; + // get full screen image + if (GraphicsOutput != NULL) { + refit_call10_wrapper(GraphicsOutput->Blt, GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)Image->PixelData, + EfiBltVideoToBltBuffer, XPos, YPos, 0, 0, + Image->Width, Image->Height, 0); + } else if (UgaDraw != NULL) { + refit_call10_wrapper(UgaDraw->Blt, UgaDraw, (EFI_UGA_PIXEL *)Image->PixelData, + EfiUgaVideoToBltBuffer, XPos, YPos, 0, 0, Image->Width, + Image->Height, 0); + } + return Image; } // EG_IMAGE * egCopyScreenArea() // @@ -531,6 +639,7 @@ Image = egCopyScreen(); if (Image == NULL) { Print(L"Error: Unable to take screen shot\n"); + LOG(1, LOG_LINE_NORMAL, L"Error: Unable to take screen shot (Image is NULL)"); goto bailout_wait; } @@ -539,6 +648,7 @@ egFreeImage(Image); if (FileData == NULL) { Print(L"Error egEncodeBMP returned NULL\n"); + LOG(1, LOG_LINE_NORMAL, L"Error: egEncodeBMP returned NULL"); goto bailout_wait; } diff -Nru refind-0.12.0/Make.common refind-0.13.2/Make.common --- refind-0.12.0/Make.common 2018-07-15 21:55:15.000000000 +0000 +++ refind-0.13.2/Make.common 2021-03-13 18:40:52.000000000 +0000 @@ -60,7 +60,7 @@ # # ...for both GNU-EFI and TianoCore.... -OPTIMFLAGS = -Os -fno-strict-aliasing +OPTIMFLAGS = -Os -fno-strict-aliasing -fno-tree-loop-distribute-patterns CFLAGS = $(OPTIMFLAGS) -fno-stack-protector -fshort-wchar -Wall # ...for GNU-EFI.... diff -Nru refind-0.12.0/mountesp refind-0.13.2/mountesp --- refind-0.12.0/mountesp 2018-08-24 13:22:28.000000000 +0000 +++ refind-0.13.2/mountesp 2021-02-28 23:16:44.000000000 +0000 @@ -15,6 +15,7 @@ # # Revision history: # +# 0.13.2 -- Fixed bug that caused failure with macOS 11.0 ("Big Sur") # 0.9.3 -- Initial release (with rEFInd 0.9.3) # Mount the ESP at /Volumes/ESP or determine its current mount @@ -22,7 +23,7 @@ MountOSXESP() { # Identify the ESP. Note: This returns the FIRST ESP found; # if the system has multiple disks, this could be wrong! - Temp=$(mount | sed -n -E "/^(\/dev\/disk[0-9]+s[0-9]+) on \/ \(.*$/s//\1/p") + Temp=$(mount | sed -n -E "/^(\/dev\/disk.*) on \/ \(.*$/s//\1/p") if [ $Temp ]; then Temp=$(diskutil list | grep " EFI " | grep -o 'disk.*' | head -n 1) if [ -z $Temp ]; then diff -Nru refind-0.12.0/mvrefind refind-0.13.2/mvrefind --- refind-0.12.0/mvrefind 2018-10-23 13:45:08.000000000 +0000 +++ refind-0.13.2/mvrefind 2021-02-28 23:16:44.000000000 +0000 @@ -150,7 +150,7 @@ exit 1 fi if [[ $EspTargetDir != "/efi/boot" && $EspTargetDir != "/efi/microsoft/boot" ]] ; then - Efibootmgr=`which efibootmgr 2> /dev/null` + Efibootmgr="$(command -v efibootmgr 2> /dev/null)" if [[ ! -f $Efibootmgr ]] ; then echo "Moving to a non-default directory requires a working efibootmgr utility, but" echo "one can't be found! Aborting!" @@ -219,18 +219,17 @@ # Create a BOOT.CSV file in the same directory as rEFInd, to help in recovery # should the system's boot entry list be lost CreateBootCsvFile() { - IConv=`which iconv 2> /dev/null` + IConv="$(command -v iconv 2> /dev/null)" if [[ -x "$IConv" && -d "$TargetDir" ]] ; then echo "$Target,rEFInd boot manager,,This is the boot entry for rEFInd" | \ $IConv -t UCS-2 > "$TargetDir/BOOT.CSV" fi - exit } # CreateBootCsvFile() # If necessary, create a new NVRAM entry for the new location AddNvramEntry() { InstallIt="0" - Efibootmgr=`which efibootmgr 2> /dev/null` + Efibootmgr="$(command -v efibootmgr 2> /dev/null)" InstallDisk=`grep $InstallDir /etc/mtab | grep -v autofs | cut -d " " -f 1 | cut -c 1-8` PartNum=`grep $InstallDir /etc/mtab | grep -v autofs | cut -d " " -f 1 | cut -c 9-10` diff -Nru refind-0.12.0/NEWS.txt refind-0.13.2/NEWS.txt --- refind-0.12.0/NEWS.txt 2020-03-13 12:41:26.000000000 +0000 +++ refind-0.13.2/NEWS.txt 2021-03-13 23:57:36.000000000 +0000 @@ -1,3 +1,158 @@ +0.13.2 (3/13/2021): +------------------- + +- Fixed memory management bugs that could cause rEFInd to hang under + certain unpredictable circumstances. + +- Fixed potential memory management bugs that could cause HFS+, ext2fs, and + ext4fs drivers to misbehave. + +- Fixed bug (introduced with version 0.13.1) that caused rEFInd to fail to + load properly signed EFI drivers when Secure Boot was enabled and used via + Shim. + +- Fixed bug in mvrefind that would cause it to fail to create a new + NVRAM entry to point to the moved rEFInd. + +- Fixed bug that caused mountesp script to fail in macOS 11.0 ("Big Sur"). + +- Improved Secure Boot support in refind-install script: The script can now + install a Secure Boot key using mokutil, which can slightly simplify the + MOK setup. It also now creates a backup NVRAM entry to boot directly, + rather than via a Shim, if installed with the --shim option. This should + help on systems with custom Secure Boot key sets that don't rely on Shim, + but with a stray Shim binary present on the ESP that a package script will + detect and pass as a --shim option to rEFInd. If this configuration causes + a Secure Boot failure on launching Shim, then it should fail over to the + direct boot and be fine. (Some EFIs will hang rather than fail over to the + next entry, though, and this change won't help with them.) + +- More logging improvements, including bumping the maximum log_level from 3 + to 4. + +- Improved separation of filesystem name vs. partition name internally. This + can make for more accurate OS icon choices in some situations, such as if + both names are set but only the partition name contains useful + information. + +- Previously, when setting "use_nvram false", rEFInd would use the "vars" + subdirectory of its own directory; and if that was unwritable (say, if it + was on an HFS+ volume), then rEFInd would not store its own variables, + such as PreviousBoot and HiddenTags. With this version, rEFInd will now + use the "vars" subdirectory of rEFInd's own directory; if that's not + available, it will use a directory called "refind-vars" in the root + directory of the first ESP that rEFInd can locate; and if neither is + available, rEFInd will use NVRAM storage instead, as if "use_nvram true" + had been set. + +- Made rEFInd's self-install code less finicky about the success of + copying configuration, icons, and driver files. Previously it reported a + failure and did not modify NVRAM if any of these failed to copy. Now it + should register itself, although it does complain on screen and log + details (if logging is enabled). This can help if installing from a source + that lacks drivers for a detectable Linux filesystem (ext2/3/4fs, + ReiserFS, Btrfs, or HFS+). + +- Adjusted reporting of SIP values; added back 0x77 as an interpreted value + for on-screen notification when rotating through values. + +- rEFInd can now identify JFS volumes as such, if a third-party JFS driver + is installed. + +- Added support for Booster initrd files, which are named "booster*", rather + than "init*". See https://github.com/anatol/booster and/or + https://wiki.archlinux.org/index.php/Booster for more on Booster. + +- Added icon for Manjaro Linux. + +- Swapped out expired CentOS Secure Boot keys for new ones. + +- Fixed build problem with GNU-EFI 3.0.13. + +0.13.1 (2/24/2021): +------------------- + +- Removed MyLibLocateProtocol(), based on RefindPlus code, because it + caused at least one Mac to hang. + +- Fixed bug that could cause rEFInd to misbehave or hang if a disabled + manual boot stanza referenced a volume with a name that existed but was + unreadable. + +- Added logging feature, activated by the log_level token in refind.conf. + This takes a numeric log level (currently from 0 to 3), with 0 meaning no + logging and 1-3 meaning increasing levels of logging. This feature is + intended to be used only when debugging problems, as it can slow rEFInd + down, especially at higher log levels. + +- Updated LodePNG library to version 20201017. + +- Fixed bug that caused rEFInd to not record a reboot to an EFI NVRAM-based + boot option, thus preventing it from being made the default boot option on + the next boot, if "default_selection +" was used. + +- Added ability to write to the LoaderDevicePartUUID EFI variable, which + systemd uses to identify the ESP that was used to boot the computer, and + to mount that partition at /boot if that directory is empty and has + nothing else mounted. This rEFInd feature is activated by the + write_systemd_vars token in refind.conf, and is enabled by default. + +- Modified EfivarSetRaw(), which rEFInd uses to write system variables, to + save a variable only if it does not currently exist or does not match the + new value. This is intended to reduce wear and tear on NVRAM storage. + (Previously, rEFInd did this with its own PreviousBoot variable, but this + change expands this functionality to apply to all EFI variables that + rEFInd might write.) + +- Uncommented use_nvram from refind.conf-sample and set it to false. This + change will cause new rEFInd installations to save rEFInd's own variables + (PreviousBoot, HiddenTags, and others) on disk rather than in NVRAM. + Existing installations won't be affected by this change. The point is to + try to save wear and tear on NVRAM storage; however, users who install + rEFInd to an HFS+ volume will need to comment out use_nvram or set it to + true in order to use features that rely on rEFInd's variable storage. + +- Additional replacements of "which" with "command -v" in mvrefind. + +0.13.0 (2/15/2021): +------------------- + +- Fixed compile problem with GCC version 10. + +- Fixed compile problems with GCC 9 and the Btrfs and HFS+ drivers under + EDK2. + +- Replaced use of "which" with "command -v" in shell scripts. + +- Fixed detection of UUID on ext2/3/4 filesystems; old code incorrectly + marked filesystems with different UUIDs but the same label as duplicates. + +- Fixed bug that caused mouse pointer to disappear on 64-bit systems when + it was moved, when mouse pointer support was active. + +- Fixed memory management bug that could cause rEFInd to hang in some + situations. + +- Fixed bug that could cause EFI 1.x (UGA graphics) systems to hang if + the refind.conf "resolution" line specified only one parameter. (This + is valid for EFI 2.x/GOP graphics systems, but not for UGA graphics.) + This misconfiguration, if found, is now ignored. + +- Imported improvements from RefindPlus + (https://github.com/dakanji/refindplus): + - Expand list of supported Apple SIP/CSR values + - Add support for EXT4_FEATURE_INCOMPAT_ENCRYPT flag to ext4fs driver + - Improved detection of video modes + - New "max" option to "resolution" parameter in refind.conf (is + default in RefindPlus, but configurable option in rEFInd) + +- Implemented new feature enabling rEFInd to set the EFI BootNext variable + and reboot into an EFI-defined boot loader. Such options are + auto-scanned if the new "firmware" option to the "scanfor" token is used; + and manual boot stanzas can use the new "firmware_bootnum" token, which + takes a hexadecimal number corresponding to the Boot#### number shown by + efibootmgr in Linux or under the EFI boot order editor in rEFInd. + 0.12.0 (3/13/2020): ------------------- diff -Nru refind-0.12.0/refind/apple.c refind-0.13.2/refind/apple.c --- refind-0.12.0/refind/apple.c 2017-08-04 19:16:32.000000000 +0000 +++ refind-0.13.2/refind/apple.c 2021-03-08 03:50:07.000000000 +0000 @@ -24,15 +24,16 @@ #include "lib.h" #include "screen.h" #include "apple.h" +#include "log.h" #include "mystrings.h" #include "../include/refit_call_wrapper.h" -CHAR16 gCsrStatus[256]; +CHAR16 gCsrStatus[512]; -// Get CSR (Apple's System Integrity Protection [SIP], or "rootless") status -// information. If the variable is not present and the firmware is Apple, fake -// it and claim it's enabled, since that's how OS X 10.11 treats a system with -// the variable absent. +// Get CSR (Apple's Configurable Security Restrictions; aka System Integrity +// Protection [SIP], or "rootless") status information. If the variable is not +// present and the firmware is Apple, fake it and claim it's enabled, since +// that's how OS X 10.11 treats a system with the variable absent. EFI_STATUS GetCsrStatus(UINT32 *CsrStatus) { UINT32 *ReturnValue = NULL; UINTN CsrLength; @@ -67,7 +68,11 @@ case SIP_ENABLED: SPrint(gCsrStatus, 255, L" System Integrity Protection is enabled (0x%02x)", CsrStatus); break; - case SIP_DISABLED: + case SIP_DISABLED_10: + SPrint(gCsrStatus, 255, L" System Integrity Protection is disabled for OS X 10.x (0x%02x)", + CsrStatus); + break; + case SIP_DISABLED_11: SPrint(gCsrStatus, 255, L" System Integrity Protection is disabled (0x%02x)", CsrStatus); break; default: @@ -88,6 +93,7 @@ EFI_GUID CsrGuid = CSR_GUID; EFI_STATUS Status; + LOG(1, LOG_LINE_SEPARATOR, L"Rotating CSR value"); Status = GetCsrStatus(&CurrentValue); if ((Status == EFI_SUCCESS) && GlobalConfig.CsrValues) { ListItem = GlobalConfig.CsrValues; @@ -98,12 +104,18 @@ } else { TargetCsr = ListItem->Next->Value; } + LOG(1, LOG_LINE_NORMAL, L"CSR value was 0x%04x; setting to 0x%04x", CurrentValue, TargetCsr); Status = EfivarSetRaw(&CsrGuid, L"csr-active-config", (CHAR8 *) &TargetCsr, 4, TRUE); - if (Status == EFI_SUCCESS) + if (Status == EFI_SUCCESS) { RecordgCsrStatus(TargetCsr, TRUE); - else + LOG(2, LOG_LINE_NORMAL, L"Successful setting of CSR value of 0x%04x", TargetCsr); + } else { SPrint(gCsrStatus, 255, L" Error setting System Integrity Protection code."); - } // if + LOG(1, LOG_LINE_NORMAL, gCsrStatus); + } + } else { + LOG(1, LOG_LINE_NORMAL, L"Could not retrieve CSR value or csr_values not set"); + } // if/else } // VOID RotateCsrValue() @@ -135,16 +147,20 @@ EFI_GUID apple_set_os_guid = EFI_APPLE_SET_OS_PROTOCOL_GUID; EfiAppleSetOsInterface *SetOs = NULL; + LOG(1, LOG_LINE_NORMAL, L"Setting Apple OS information, if applicable"); Status = refit_call3_wrapper(BS->LocateProtocol, &apple_set_os_guid, NULL, (VOID**) &SetOs); // If not a Mac, ignore the call.... - if ((Status != EFI_SUCCESS) || (!SetOs)) + if ((Status != EFI_SUCCESS) || (!SetOs)) { + LOG(2, LOG_LINE_NORMAL, L"Not a Mac; not setting Apple OS information"); return EFI_SUCCESS; + } if ((SetOs->Version != 0) && GlobalConfig.SpoofOSXVersion) { AppleOSVersion = StrDuplicate(L"Mac OS X"); MergeStrings(&AppleOSVersion, GlobalConfig.SpoofOSXVersion, ' '); if (AppleOSVersion) { + LOG(2, LOG_LINE_NORMAL, L"Setting Apple OS information to '%s'", AppleOSVersion); AppleOSVersion8 = AllocateZeroPool((StrLen(AppleOSVersion) + 1) * sizeof(CHAR8)); UnicodeStrToAsciiStr(AppleOSVersion, AppleOSVersion8); if (AppleOSVersion8) { @@ -165,4 +181,4 @@ Print(L"Unable to set firmware boot type!\n"); return (Status); -} // EFI_STATUS SetAppleOSInfo() \ No newline at end of file +} // EFI_STATUS SetAppleOSInfo() diff -Nru refind-0.12.0/refind/apple.h refind-0.13.2/refind/apple.h --- refind-0.12.0/refind/apple.h 2015-11-29 18:54:48.000000000 +0000 +++ refind-0.13.2/refind/apple.h 2021-03-08 03:50:07.000000000 +0000 @@ -1,8 +1,8 @@ /* * refind/apple.h - * + * * Copyright (c) 2015 Roderick W. Smith - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -23,36 +23,55 @@ // The constants related to Apple's System Integrity Protection (SIP).... #define CSR_GUID { 0x7c436110, 0xab2a, 0x4bbb, { 0xa8, 0x80, 0xfe, 0x41, 0x99, 0x5c, 0x9f, 0x82 } }; -// These codes are returned in the first byte of the csr-active-config variable -#define CSR_ALLOW_UNTRUSTED_KEXTS 0x01 -#define CSR_ALLOW_UNRESTRICTED_FS 0x02 -#define CSR_ALLOW_TASK_FOR_PID 0x04 -#define CSR_ALLOW_KERNEL_DEBUGGER 0x08 -#define CSR_ALLOW_APPLE_INTERNAL 0x10 -#define CSR_ALLOW_UNRESTRICTED_DTRACE 0x20 -#define CSR_ALLOW_UNRESTRICTED_NVRAM 0x40 -#define CSR_END_OF_LIST 0xFFFFFFFF +// These codes are returned in the first two bytes of the csr-active-config variable +#define CSR_ALLOW_UNTRUSTED_KEXTS 0x01 +#define CSR_ALLOW_UNRESTRICTED_FS 0x02 +#define CSR_ALLOW_TASK_FOR_PID 0x04 +#define CSR_ALLOW_KERNEL_DEBUGGER 0x08 +#define CSR_ALLOW_APPLE_INTERNAL 0x10 +#define CSR_ALLOW_UNRESTRICTED_DTRACE 0x20 +#define CSR_ALLOW_UNRESTRICTED_NVRAM 0x40 +#define CSR_ALLOW_DEVICE_CONFIGURATION 0x80 +#define CSR_ALLOW_ANY_RECOVERY_OS 0x100 +#define CSR_ALLOW_UNAPPROVED_KEXTS 0x200 +#define CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE 0x400 +#define CSR_ALLOW_UNAUTHENTICATED_ROOT 0x800 +#define CSR_END_OF_LIST 0xFFFFFFFF // Some summaries.... #define SIP_ENABLED CSR_ALLOW_APPLE_INTERNAL -#define SIP_DISABLED (CSR_ALLOW_UNRESTRICTED_NVRAM | \ - CSR_ALLOW_UNRESTRICTED_DTRACE | \ - CSR_ALLOW_APPLE_INTERNAL | \ - CSR_ALLOW_TASK_FOR_PID | \ - CSR_ALLOW_UNRESTRICTED_FS | \ - CSR_ALLOW_UNTRUSTED_KEXTS) +// SIP was disabled with a value of 0x77 in macOS 10.x.... +#define SIP_DISABLED_10 (CSR_ALLOW_UNRESTRICTED_NVRAM | \ + CSR_ALLOW_UNRESTRICTED_DTRACE | \ + CSR_ALLOW_APPLE_INTERNAL | \ + CSR_ALLOW_TASK_FOR_PID | \ + CSR_ALLOW_UNRESTRICTED_FS | \ + CSR_ALLOW_UNTRUSTED_KEXTS) +// Starting with macOS 11.0, SIP requires 0x877 to be fully disabled.... +#define SIP_DISABLED_11 (CSR_ALLOW_UNRESTRICTED_NVRAM | \ + CSR_ALLOW_UNRESTRICTED_DTRACE | \ + CSR_ALLOW_APPLE_INTERNAL | \ + CSR_ALLOW_TASK_FOR_PID | \ + CSR_ALLOW_UNRESTRICTED_FS | \ + CSR_ALLOW_UNTRUSTED_KEXTS | \ + CSR_ALLOW_UNAUTHENTICATED_ROOT) #define CSR_MAX_LEGAL_VALUE (CSR_ALLOW_UNTRUSTED_KEXTS | \ CSR_ALLOW_UNRESTRICTED_FS | \ CSR_ALLOW_TASK_FOR_PID | \ CSR_ALLOW_KERNEL_DEBUGGER | \ CSR_ALLOW_APPLE_INTERNAL | \ CSR_ALLOW_UNRESTRICTED_DTRACE | \ - CSR_ALLOW_UNRESTRICTED_NVRAM) + CSR_ALLOW_UNRESTRICTED_NVRAM | \ + CSR_ALLOW_DEVICE_CONFIGURATION | \ + CSR_ALLOW_ANY_RECOVERY_OS | \ + CSR_ALLOW_UNAPPROVED_KEXTS | \ + CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE | \ + CSR_ALLOW_UNAUTHENTICATED_ROOT) -extern CHAR16 gCsrStatus[256]; +extern CHAR16 gCsrStatus[512]; EFI_STATUS GetCsrStatus(UINT32 *CsrValue); VOID RecordgCsrStatus(UINT32 CsrStatus, BOOLEAN DisplayMessage); VOID RotateCsrValue(VOID); EFI_STATUS SetAppleOSInfo(); -#endif \ No newline at end of file +#endif diff -Nru refind-0.12.0/refind/config.c refind-0.13.2/refind/config.c --- refind-0.12.0/refind/config.c 2020-03-07 18:51:43.000000000 +0000 +++ refind-0.13.2/refind/config.c 2021-03-08 03:50:07.000000000 +0000 @@ -35,7 +35,7 @@ */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3) or (at your option) any later version. @@ -59,6 +59,7 @@ #include "global.h" #include "lib.h" #include "icns.h" +#include "log.h" #include "menu.h" #include "config.h" #include "screen.h" @@ -93,16 +94,18 @@ EFI_FILE_HANDLE FileHandle; EFI_FILE_INFO *FileInfo; UINT64 ReadSize; - CHAR16 Message[256]; + CHAR16 *Message; File->Buffer = NULL; File->BufferSize = 0; // read the file, allocating a buffer on the way Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0); - SPrint(Message, 255, L"while loading the file '%s'", FileName); - if (CheckError(Status, Message)) + Message = PoolPrint(L"while loading the file '%s'", FileName); + if (CheckError(Status, Message)) { + MyFreePool(Message); return Status; + } FileInfo = LibFileInfo(FileHandle); if (FileInfo == NULL) { @@ -124,6 +127,7 @@ Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &File->BufferSize, File->Buffer); if (CheckError(Status, Message)) { MyFreePool(File->Buffer); + MyFreePool(Message); File->Buffer = NULL; refit_call1_wrapper(FileHandle->Close, FileHandle); return Status; @@ -239,33 +243,33 @@ // Also modifies *p **IF** the first and second characters are both // quotes ('"'); it deletes one of them. static BOOLEAN KeepReading(IN OUT CHAR16 *p, IN OUT BOOLEAN *IsQuoted) { - BOOLEAN MoreToRead = FALSE; - CHAR16 *Temp = NULL; + BOOLEAN MoreToRead = FALSE; + CHAR16 *Temp = NULL; - if ((p == NULL) || (IsQuoted == NULL)) - return FALSE; + if ((p == NULL) || (IsQuoted == NULL)) + return FALSE; - if (*p == L'\0') - return FALSE; + if (*p == L'\0') + return FALSE; - if ((*p != ' ' && *p != '\t' && *p != '=' && *p != '#' && *p != ',') || *IsQuoted) { - MoreToRead = TRUE; - } - if (*p == L'"') { - if (p[1] == L'"') { - Temp = StrDuplicate(&p[1]); - if (Temp != NULL) { - StrCpy(p, Temp); - FreePool(Temp); - } - MoreToRead = TRUE; - } else { - *IsQuoted = !(*IsQuoted); - MoreToRead = FALSE; - } // if/else second character is a quote - } // if first character is a quote + if ((*p != ' ' && *p != '\t' && *p != '=' && *p != '#' && *p != ',') || *IsQuoted) { + MoreToRead = TRUE; + } + if (*p == L'"') { + if (p[1] == L'"') { + Temp = StrDuplicate(&p[1]); + if (Temp != NULL) { + StrCpy(p, Temp); + FreePool(Temp); + } + MoreToRead = TRUE; + } else { + *IsQuoted = !(*IsQuoted); + MoreToRead = FALSE; + } // if/else second character is a quote + } // if first character is a quote - return MoreToRead; + return MoreToRead; } // BOOLEAN KeepReading() // @@ -348,23 +352,23 @@ // non-keyword token is "+", the list is added to the existing target string; otherwise, // the tokens replace the current string. static VOID HandleStrings(IN CHAR16 **TokenList, IN UINTN TokenCount, OUT CHAR16 **Target) { - UINTN i; - BOOLEAN AddMode = FALSE; + UINTN i; + BOOLEAN AddMode = FALSE; + + if ((TokenCount > 2) && (StrCmp(TokenList[1], L"+") == 0)) { + AddMode = TRUE; + } - if ((TokenCount > 2) && (StrCmp(TokenList[1], L"+") == 0)) { - AddMode = TRUE; - } - - if ((*Target != NULL) && !AddMode) { - FreePool(*Target); - *Target = NULL; - } // if - for (i = 1; i < TokenCount; i++) { - if ((i != 1) || !AddMode) { - CleanUpPathNameSlashes(TokenList[i]); - MergeStrings(Target, TokenList[i], L','); - } // if - } // for + if ((*Target != NULL) && !AddMode) { + FreePool(*Target); + *Target = NULL; + } // if + for (i = 1; i < TokenCount; i++) { + if ((i != 1) || !AddMode) { + CleanUpPathNameSlashes(TokenList[i]); + MergeStrings(Target, TokenList[i], L','); + } // if + } // for } // static VOID HandleStrings() // Handle a parameter with a series of hexadecimal arguments, to replace or be added to a @@ -372,7 +376,10 @@ // any value that exceeds MaxValue. If the first non-keyword token is "+", the new list is // added to the existing Target; otherwise, the interpreted tokens replace the current // Target. -static VOID HandleHexes(IN CHAR16 **TokenList, IN UINTN TokenCount, IN UINTN MaxValue, OUT UINT32_LIST **Target) { +static VOID HandleHexes(IN CHAR16 **TokenList, + IN UINTN TokenCount, + IN UINTN MaxValue, + OUT UINT32_LIST **Target) { UINTN InputIndex = 1, i; UINT32 Value; UINT32_LIST *EndOfList = NULL; @@ -415,70 +422,70 @@ // the input is a number that includes no colon, this function will return the original // number in UINTN form. static UINTN HandleTime(IN CHAR16 *TimeString) { - UINTN Hour = 0, Minute = 0, TimeLength, i = 0; + UINTN Hour = 0, Minute = 0, TimeLength, i = 0; - TimeLength = StrLen(TimeString); - while (i < TimeLength) { - if (TimeString[i] == L':') { - Hour = Minute; - Minute = 0; - } // if - if ((TimeString[i] >= L'0') && (TimeString[i] <= '9')) { - Minute *= 10; - Minute += (TimeString[i] - L'0'); - } // if - i++; - } // while - return (Hour * 60 + Minute); + TimeLength = StrLen(TimeString); + while (i < TimeLength) { + if (TimeString[i] == L':') { + Hour = Minute; + Minute = 0; + } // if + if ((TimeString[i] >= L'0') && (TimeString[i] <= '9')) { + Minute *= 10; + Minute += (TimeString[i] - L'0'); + } // if + i++; + } // while + return (Hour * 60 + Minute); } // BOOLEAN HandleTime() static BOOLEAN HandleBoolean(IN CHAR16 **TokenList, IN UINTN TokenCount) { - BOOLEAN TruthValue = TRUE; + BOOLEAN TruthValue = TRUE; - if ((TokenCount >= 2) && ((StrCmp(TokenList[1], L"0") == 0) || - MyStriCmp(TokenList[1], L"false") || - MyStriCmp(TokenList[1], L"off"))) { - TruthValue = FALSE; - } // if + if ((TokenCount >= 2) && ((StrCmp(TokenList[1], L"0") == 0) || + MyStriCmp(TokenList[1], L"false") || + MyStriCmp(TokenList[1], L"off"))) { + TruthValue = FALSE; + } // if - return TruthValue; + return TruthValue; } // BOOLEAN HandleBoolean // Sets the default boot loader IF the current time is within the bounds // defined by the third and fourth tokens in the TokenList. static VOID SetDefaultByTime(IN CHAR16 **TokenList, OUT CHAR16 **Default) { - EFI_STATUS Status; - EFI_TIME CurrentTime; - UINTN StartTime, EndTime, Now; - BOOLEAN SetIt = FALSE; - - StartTime = HandleTime(TokenList[2]); - EndTime = HandleTime(TokenList[3]); - - if ((StartTime <= LAST_MINUTE) && (EndTime <= LAST_MINUTE)) { - Status = refit_call2_wrapper(GetTime, &CurrentTime, NULL); - if (Status != EFI_SUCCESS) - return; - Now = CurrentTime.Hour * 60 + CurrentTime.Minute; - - if (Now > LAST_MINUTE) { // Shouldn't happen; just being paranoid - Print(L"Warning: Impossible system time: %d:%d\n", CurrentTime.Hour, CurrentTime.Minute); - return; - } // if impossible time - - if (StartTime < EndTime) { // Time range does NOT cross midnight - if ((Now >= StartTime) && (Now <= EndTime)) - SetIt = TRUE; - } else { // Time range DOES cross midnight - if ((Now >= StartTime) && (Now <= EndTime)) - SetIt = TRUE; - } // if/else time range crosses midnight - - if (SetIt) { - MyFreePool(*Default); - *Default = StrDuplicate(TokenList[1]); - } // if (SetIt) - } // if ((StartTime <= LAST_MINUTE) && (EndTime <= LAST_MINUTE)) + EFI_STATUS Status; + EFI_TIME CurrentTime; + UINTN StartTime, EndTime, Now; + BOOLEAN SetIt = FALSE; + + StartTime = HandleTime(TokenList[2]); + EndTime = HandleTime(TokenList[3]); + + if ((StartTime <= LAST_MINUTE) && (EndTime <= LAST_MINUTE)) { + Status = refit_call2_wrapper(GetTime, &CurrentTime, NULL); + if (Status != EFI_SUCCESS) + return; + Now = CurrentTime.Hour * 60 + CurrentTime.Minute; + + if (Now > LAST_MINUTE) { // Shouldn't happen; just being paranoid + Print(L"Warning: Impossible system time: %d:%d\n", CurrentTime.Hour, CurrentTime.Minute); + return; + } // if impossible time + + if (StartTime < EndTime) { // Time range does NOT cross midnight + if ((Now >= StartTime) && (Now <= EndTime)) + SetIt = TRUE; + } else { // Time range DOES cross midnight + if ((Now >= StartTime) && (Now <= EndTime)) + SetIt = TRUE; + } // if/else time range crosses midnight + + if (SetIt) { + MyFreePool(*Default); + *Default = StrDuplicate(TokenList[1]); + } // if (SetIt) + } // if ((StartTime <= LAST_MINUTE) && (EndTime <= LAST_MINUTE)) } // VOID SetDefaultByTime() static LOADER_ENTRY * AddPreparedLoaderEntry(LOADER_ENTRY *Entry) { @@ -499,35 +506,37 @@ // Set a few defaults only if we're loading the default file. if (MyStriCmp(FileName, GlobalConfig.ConfigFilename)) { - MyFreePool(GlobalConfig.AlsoScan); - GlobalConfig.AlsoScan = StrDuplicate(ALSO_SCAN_DIRS); - MyFreePool(GlobalConfig.DontScanDirs); - if (SelfVolume) - TempStr = GuidAsString(&(SelfVolume->PartGuid)); - MergeStrings(&TempStr, SelfDirPath, L':'); - MergeStrings(&TempStr, MEMTEST_LOCATIONS, L','); - GlobalConfig.DontScanDirs = TempStr; - MyFreePool(GlobalConfig.DontScanFiles); - GlobalConfig.DontScanFiles = StrDuplicate(DONT_SCAN_FILES); - MyFreePool(GlobalConfig.DontScanTools); - GlobalConfig.DontScanTools = NULL; - MergeStrings(&(GlobalConfig.DontScanFiles), MOK_NAMES, L','); - MergeStrings(&(GlobalConfig.DontScanFiles), FWUPDATE_NAMES, L','); - MyFreePool(GlobalConfig.DontScanVolumes); - GlobalConfig.DontScanVolumes = StrDuplicate(DONT_SCAN_VOLUMES); - GlobalConfig.WindowsRecoveryFiles = StrDuplicate(WINDOWS_RECOVERY_FILES); - GlobalConfig.MacOSRecoveryFiles = StrDuplicate(MACOS_RECOVERY_FILES); - MyFreePool(GlobalConfig.DefaultSelection); - GlobalConfig.DefaultSelection = StrDuplicate(L"+"); + MyFreePool(GlobalConfig.AlsoScan); + GlobalConfig.AlsoScan = StrDuplicate(ALSO_SCAN_DIRS); + MyFreePool(GlobalConfig.DontScanDirs); + if (SelfVolume) + TempStr = GuidAsString(&(SelfVolume->PartGuid)); + MergeStrings(&TempStr, SelfDirPath, L':'); + MergeStrings(&TempStr, MEMTEST_LOCATIONS, L','); + GlobalConfig.DontScanDirs = TempStr; + MyFreePool(GlobalConfig.DontScanFiles); + GlobalConfig.DontScanFiles = StrDuplicate(DONT_SCAN_FILES); + MyFreePool(GlobalConfig.DontScanTools); + GlobalConfig.DontScanTools = NULL; + MyFreePool(GlobalConfig.DontScanFirmware); + GlobalConfig.DontScanFirmware = NULL; + MergeStrings(&(GlobalConfig.DontScanFiles), MOK_NAMES, L','); + MergeStrings(&(GlobalConfig.DontScanFiles), FWUPDATE_NAMES, L','); + MyFreePool(GlobalConfig.DontScanVolumes); + GlobalConfig.DontScanVolumes = StrDuplicate(DONT_SCAN_VOLUMES); + GlobalConfig.WindowsRecoveryFiles = StrDuplicate(WINDOWS_RECOVERY_FILES); + GlobalConfig.MacOSRecoveryFiles = StrDuplicate(MACOS_RECOVERY_FILES); + MyFreePool(GlobalConfig.DefaultSelection); + GlobalConfig.DefaultSelection = StrDuplicate(L"+"); } // if if (!FileExists(SelfDir, FileName)) { - 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; + 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); @@ -549,72 +558,84 @@ for (i = 1; i < TokenCount; i++) { FlagName = TokenList[i]; if (MyStriCmp(FlagName, L"banner")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BANNER; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BANNER; } else if (MyStriCmp(FlagName, L"label")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_LABEL; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_LABEL; } else if (MyStriCmp(FlagName, L"singleuser")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SINGLEUSER; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SINGLEUSER; } else if (MyStriCmp(FlagName, L"hwtest")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HWTEST; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HWTEST; } else if (MyStriCmp(FlagName, L"arrows")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_ARROWS; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_ARROWS; } else if (MyStriCmp(FlagName, L"hints")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HINTS; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_HINTS; } else if (MyStriCmp(FlagName, L"editor")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_EDITOR; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_EDITOR; } else if (MyStriCmp(FlagName, L"safemode")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SAFEMODE; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_SAFEMODE; } else if (MyStriCmp(FlagName, L"badges")) { - GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BADGES; + GlobalConfig.HideUIFlags |= HIDEUI_FLAG_BADGES; } else if (MyStriCmp(FlagName, L"all")) { - GlobalConfig.HideUIFlags = HIDEUI_FLAG_ALL; + GlobalConfig.HideUIFlags = HIDEUI_FLAG_ALL; } else { Print(L" unknown hideui flag: '%s'\n", FlagName); } } } else if (MyStriCmp(TokenList[0], L"icons_dir")) { - HandleString(TokenList, TokenCount, &(GlobalConfig.IconsDir)); + HandleString(TokenList, TokenCount, &(GlobalConfig.IconsDir)); } else if (MyStriCmp(TokenList[0], L"scanfor")) { - for (i = 0; i < NUM_SCAN_OPTIONS; i++) { - if (i < TokenCount) - GlobalConfig.ScanFor[i] = TokenList[i][0]; - else - GlobalConfig.ScanFor[i] = ' '; - } + for (i = 0; i < NUM_SCAN_OPTIONS; i++) { + if (i < TokenCount) + GlobalConfig.ScanFor[i] = TokenList[i][0]; + else + GlobalConfig.ScanFor[i] = ' '; + } } else if (MyStriCmp(TokenList[0], L"use_nvram")) { - GlobalConfig.UseNvram = HandleBoolean(TokenList, TokenCount); + GlobalConfig.UseNvram = HandleBoolean(TokenList, TokenCount); } else if (MyStriCmp(TokenList[0], L"uefi_deep_legacy_scan")) { - GlobalConfig.DeepLegacyScan = HandleBoolean(TokenList, TokenCount); + GlobalConfig.DeepLegacyScan = HandleBoolean(TokenList, TokenCount); } else if (MyStriCmp(TokenList[0], L"scan_delay") && (TokenCount == 2)) { - HandleInt(TokenList, TokenCount, &(GlobalConfig.ScanDelay)); + HandleInt(TokenList, TokenCount, &(GlobalConfig.ScanDelay)); + + } else if (MyStriCmp(TokenList[0], L"log_level") && (TokenCount == 2)) { + HandleInt(TokenList, TokenCount, &(GlobalConfig.LogLevel)); } else if (MyStriCmp(TokenList[0], L"also_scan_dirs")) { - HandleStrings(TokenList, TokenCount, &(GlobalConfig.AlsoScan)); + HandleStrings(TokenList, TokenCount, &(GlobalConfig.AlsoScan)); - } else if (MyStriCmp(TokenList[0], L"don't_scan_volumes") || MyStriCmp(TokenList[0], L"dont_scan_volumes")) { - // Note: Don't use HandleStrings() because it modifies slashes, which might be present in volume name - MyFreePool(GlobalConfig.DontScanVolumes); - GlobalConfig.DontScanVolumes = NULL; - for (i = 1; i < TokenCount; i++) { - MergeStrings(&GlobalConfig.DontScanVolumes, TokenList[i], L','); - } + } else if (MyStriCmp(TokenList[0], L"don't_scan_volumes") || + MyStriCmp(TokenList[0], L"dont_scan_volumes")) { + // Note: Don't use HandleStrings() because it modifies slashes, + // which might be present in volume name + MyFreePool(GlobalConfig.DontScanVolumes); + GlobalConfig.DontScanVolumes = NULL; + for (i = 1; i < TokenCount; i++) { + MergeStrings(&GlobalConfig.DontScanVolumes, TokenList[i], L','); + } - } else if (MyStriCmp(TokenList[0], L"don't_scan_dirs") || MyStriCmp(TokenList[0], L"dont_scan_dirs")) { + } else if (MyStriCmp(TokenList[0], L"don't_scan_dirs") || + MyStriCmp(TokenList[0], L"dont_scan_dirs")) { HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanDirs)); - } else if (MyStriCmp(TokenList[0], L"don't_scan_files") || MyStriCmp(TokenList[0], L"dont_scan_files")) { - HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanFiles)); - - } else if (MyStriCmp(TokenList[0], L"don't_scan_tools") || MyStriCmp(TokenList[0], L"dont_scan_tools")) { - HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanTools)); + } else if (MyStriCmp(TokenList[0], L"don't_scan_files") || + MyStriCmp(TokenList[0], L"dont_scan_files")) { + HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanFiles)); + + } else if (MyStriCmp(TokenList[0], L"don't_scan_firmware") || + MyStriCmp(TokenList[0], L"dont_scan_firmware")) { + HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanFirmware)); + + } else if (MyStriCmp(TokenList[0], L"don't_scan_tools") || + MyStriCmp(TokenList[0], L"dont_scan_tools")) { + HandleStrings(TokenList, TokenCount, &(GlobalConfig.DontScanTools)); } else if (MyStriCmp(TokenList[0], L"windows_recovery_files")) { - HandleStrings(TokenList, TokenCount, &(GlobalConfig.WindowsRecoveryFiles)); + HandleStrings(TokenList, TokenCount, &(GlobalConfig.WindowsRecoveryFiles)); } else if (MyStriCmp(TokenList[0], L"scan_driver_dirs")) { HandleStrings(TokenList, TokenCount, &(GlobalConfig.DriverDirs)); @@ -667,89 +688,94 @@ } // showtools options } else if (MyStriCmp(TokenList[0], L"banner")) { - HandleString(TokenList, TokenCount, &(GlobalConfig.BannerFileName)); + HandleString(TokenList, TokenCount, &(GlobalConfig.BannerFileName)); } else if (MyStriCmp(TokenList[0], L"banner_scale") && (TokenCount == 2)) { - if (MyStriCmp(TokenList[1], L"noscale")) { - GlobalConfig.BannerScale = BANNER_NOSCALE; - } else if (MyStriCmp(TokenList[1], L"fillscreen") || MyStriCmp(TokenList[1], L"fullscreen")) { - GlobalConfig.BannerScale = BANNER_FILLSCREEN; - } else { - Print(L" unknown banner_type flag: '%s'\n", TokenList[1]); - } // if/else + if (MyStriCmp(TokenList[1], L"noscale")) { + GlobalConfig.BannerScale = BANNER_NOSCALE; + } else if (MyStriCmp(TokenList[1], L"fillscreen") || MyStriCmp(TokenList[1], L"fullscreen")) { + GlobalConfig.BannerScale = BANNER_FILLSCREEN; + } else { + Print(L" unknown banner_type flag: '%s'\n", TokenList[1]); + } // if/else } else if (MyStriCmp(TokenList[0], L"small_icon_size") && (TokenCount == 2)) { - HandleInt(TokenList, TokenCount, &i); - if (i >= 32) { - GlobalConfig.IconSizes[ICON_SIZE_SMALL] = i; - HaveResized = TRUE; - } + HandleInt(TokenList, TokenCount, &i); + if (i >= 32) { + GlobalConfig.IconSizes[ICON_SIZE_SMALL] = i; + HaveResized = TRUE; + } } else if (MyStriCmp(TokenList[0], L"big_icon_size") && (TokenCount == 2)) { - HandleInt(TokenList, TokenCount, &i); - if (i >= 32) { - GlobalConfig.IconSizes[ICON_SIZE_BIG] = i; - GlobalConfig.IconSizes[ICON_SIZE_BADGE] = i / 4; - HaveResized = TRUE; - } + HandleInt(TokenList, TokenCount, &i); + if (i >= 32) { + GlobalConfig.IconSizes[ICON_SIZE_BIG] = i; + GlobalConfig.IconSizes[ICON_SIZE_BADGE] = i / 4; + HaveResized = TRUE; + } } else if (MyStriCmp(TokenList[0], L"mouse_size") && (TokenCount == 2)) { - HandleInt(TokenList, TokenCount, &i); - if (i >= DEFAULT_MOUSE_SIZE) { - GlobalConfig.IconSizes[ICON_SIZE_MOUSE] = i; - } + HandleInt(TokenList, TokenCount, &i); + if (i >= DEFAULT_MOUSE_SIZE) { + GlobalConfig.IconSizes[ICON_SIZE_MOUSE] = i; + } } else if (MyStriCmp(TokenList[0], L"selection_small")) { - HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionSmallFileName)); + HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionSmallFileName)); } else if (MyStriCmp(TokenList[0], L"selection_big")) { - HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionBigFileName)); + HandleString(TokenList, TokenCount, &(GlobalConfig.SelectionBigFileName)); } else if (MyStriCmp(TokenList[0], L"default_selection")) { - if (TokenCount == 4) { - SetDefaultByTime(TokenList, &(GlobalConfig.DefaultSelection)); - } else { - HandleString(TokenList, TokenCount, &(GlobalConfig.DefaultSelection)); - } + if (TokenCount == 4) { + SetDefaultByTime(TokenList, &(GlobalConfig.DefaultSelection)); + } else { + HandleString(TokenList, TokenCount, &(GlobalConfig.DefaultSelection)); + } } else if (MyStriCmp(TokenList[0], L"textonly")) { - GlobalConfig.TextOnly = HandleBoolean(TokenList, TokenCount); + GlobalConfig.TextOnly = HandleBoolean(TokenList, TokenCount); } else if (MyStriCmp(TokenList[0], L"textmode")) { - HandleInt(TokenList, TokenCount, &(GlobalConfig.RequestedTextMode)); + HandleInt(TokenList, TokenCount, &(GlobalConfig.RequestedTextMode)); } else if (MyStriCmp(TokenList[0], L"resolution") && ((TokenCount == 2) || (TokenCount == 3))) { - GlobalConfig.RequestedScreenWidth = Atoi(TokenList[1]); - if (TokenCount == 3) - GlobalConfig.RequestedScreenHeight = Atoi(TokenList[2]); - else - GlobalConfig.RequestedScreenHeight = 0; + if (MyStriCmp(TokenList[1], L"max")) { + GlobalConfig.RequestedScreenWidth = MAX_RES_CODE; + GlobalConfig.RequestedScreenHeight = MAX_RES_CODE; + } else { + GlobalConfig.RequestedScreenWidth = Atoi(TokenList[1]); + if (TokenCount == 3) + GlobalConfig.RequestedScreenHeight = Atoi(TokenList[2]); + else + GlobalConfig.RequestedScreenHeight = 0; + } } else if (MyStriCmp(TokenList[0], L"screensaver")) { - HandleInt(TokenList, TokenCount, &(GlobalConfig.ScreensaverTime)); + HandleInt(TokenList, TokenCount, &(GlobalConfig.ScreensaverTime)); } else if (MyStriCmp(TokenList[0], L"use_graphics_for")) { - if ((TokenCount == 2) || ((TokenCount > 2) && (!MyStriCmp(TokenList[1], L"+")))) - GlobalConfig.GraphicsFor = 0; - for (i = 1; i < TokenCount; i++) { - if (MyStriCmp(TokenList[i], L"osx")) { - GlobalConfig.GraphicsFor |= GRAPHICS_FOR_OSX; - } else if (MyStriCmp(TokenList[i], L"linux")) { - GlobalConfig.GraphicsFor |= GRAPHICS_FOR_LINUX; - } else if (MyStriCmp(TokenList[i], L"elilo")) { - GlobalConfig.GraphicsFor |= GRAPHICS_FOR_ELILO; - } else if (MyStriCmp(TokenList[i], L"grub")) { - GlobalConfig.GraphicsFor |= GRAPHICS_FOR_GRUB; - } else if (MyStriCmp(TokenList[i], L"windows")) { - GlobalConfig.GraphicsFor |= GRAPHICS_FOR_WINDOWS; - } - } // for (graphics_on tokens) + if ((TokenCount == 2) || ((TokenCount > 2) && (!MyStriCmp(TokenList[1], L"+")))) + GlobalConfig.GraphicsFor = 0; + for (i = 1; i < TokenCount; i++) { + if (MyStriCmp(TokenList[i], L"osx")) { + GlobalConfig.GraphicsFor |= GRAPHICS_FOR_OSX; + } else if (MyStriCmp(TokenList[i], L"linux")) { + GlobalConfig.GraphicsFor |= GRAPHICS_FOR_LINUX; + } else if (MyStriCmp(TokenList[i], L"elilo")) { + GlobalConfig.GraphicsFor |= GRAPHICS_FOR_ELILO; + } else if (MyStriCmp(TokenList[i], L"grub")) { + GlobalConfig.GraphicsFor |= GRAPHICS_FOR_GRUB; + } else if (MyStriCmp(TokenList[i], L"windows")) { + GlobalConfig.GraphicsFor |= GRAPHICS_FOR_WINDOWS; + } + } // for (graphics_on tokens) } else if (MyStriCmp(TokenList[0], L"font") && (TokenCount == 2)) { - egLoadFont(TokenList[1]); + egLoadFont(TokenList[1]); } else if (MyStriCmp(TokenList[0], L"scan_all_linux_kernels")) { - GlobalConfig.ScanAllLinux = HandleBoolean(TokenList, TokenCount); + GlobalConfig.ScanAllLinux = HandleBoolean(TokenList, TokenCount); } else if (MyStriCmp(TokenList[0], L"fold_linux_kernels")) { GlobalConfig.FoldLinuxKernels = HandleBoolean(TokenList, TokenCount); @@ -758,10 +784,10 @@ HandleStrings(TokenList, TokenCount, &(GlobalConfig.ExtraKernelVersionStrings)); } else if (MyStriCmp(TokenList[0], L"max_tags")) { - HandleInt(TokenList, TokenCount, &(GlobalConfig.MaxTags)); + HandleInt(TokenList, TokenCount, &(GlobalConfig.MaxTags)); } else if (MyStriCmp(TokenList[0], L"enable_and_lock_vmx")) { - GlobalConfig.EnableAndLockVMX = HandleBoolean(TokenList, TokenCount); + GlobalConfig.EnableAndLockVMX = HandleBoolean(TokenList, TokenCount); } else if (MyStriCmp(TokenList[0], L"spoof_osx_version")) { HandleString(TokenList, TokenCount, &(GlobalConfig.SpoofOSXVersion)); @@ -769,249 +795,274 @@ } else if (MyStriCmp(TokenList[0], L"csr_values")) { HandleHexes(TokenList, TokenCount, CSR_MAX_LEGAL_VALUE, &(GlobalConfig.CsrValues)); - } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) && MyStriCmp(FileName, GlobalConfig.ConfigFilename)) { - if (!MyStriCmp(TokenList[1], FileName)) { - ReadConfig(TokenList[1]); - } + } else if (MyStriCmp(TokenList[0], L"write_systemd_vars")) { + GlobalConfig.WriteSystemdVars = HandleBoolean(TokenList, TokenCount); + + } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) && + MyStriCmp(FileName, GlobalConfig.ConfigFilename)) { + if (!MyStriCmp(TokenList[1], FileName)) { + ReadConfig(TokenList[1]); + } } else if (MyStriCmp(TokenList[0], L"enable_mouse")) { - GlobalConfig.EnableMouse = HandleBoolean(TokenList, TokenCount); - if(GlobalConfig.EnableMouse) { - GlobalConfig.EnableTouch = FALSE; - } + GlobalConfig.EnableMouse = HandleBoolean(TokenList, TokenCount); + if (GlobalConfig.EnableMouse) { + GlobalConfig.EnableTouch = FALSE; + } } else if (MyStriCmp(TokenList[0], L"enable_touch")) { - GlobalConfig.EnableTouch = HandleBoolean(TokenList, TokenCount); - if(GlobalConfig.EnableTouch) { - GlobalConfig.EnableMouse = FALSE; - } + GlobalConfig.EnableTouch = HandleBoolean(TokenList, TokenCount); + if (GlobalConfig.EnableTouch) { + GlobalConfig.EnableMouse = FALSE; + } } else if (MyStriCmp(TokenList[0], L"mouse_speed") && (TokenCount == 2)) { - HandleInt(TokenList, TokenCount, &i); - if (i < 1) - i = 1; - if (i > 32) - i = 32; - GlobalConfig.MouseSpeed = i; + HandleInt(TokenList, TokenCount, &i); + if (i < 1) + i = 1; + if (i > 32) + i = 32; + GlobalConfig.MouseSpeed = i; } FreeTokenLine(&TokenList, &TokenCount); } if ((GlobalConfig.DontScanFiles) && (GlobalConfig.WindowsRecoveryFiles)) - MergeStrings(&(GlobalConfig.DontScanFiles), GlobalConfig.WindowsRecoveryFiles, L','); + 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; + Print(L"Icons directory doesn't exist; setting textonly = TRUE!\n"); + GlobalConfig.TextOnly = TRUE; } } /* VOID ReadConfig() */ static VOID AddSubmenu(LOADER_ENTRY *Entry, REFIT_FILE *File, REFIT_VOLUME *Volume, CHAR16 *Title) { - REFIT_MENU_SCREEN *SubScreen; - LOADER_ENTRY *SubEntry; - UINTN TokenCount; - CHAR16 **TokenList; - - SubScreen = InitializeSubScreen(Entry); - - // Set defaults for the new entry; will be modified based on lines read from the config. file.... - SubEntry = InitializeLoaderEntry(Entry); - - if ((SubEntry == NULL) || (SubScreen == NULL)) - return; - SubEntry->me.Title = StrDuplicate(Title); - - while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) { - - if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename - MyFreePool(SubEntry->LoaderPath); - SubEntry->LoaderPath = StrDuplicate(TokenList[1]); - SubEntry->Volume = Volume; - - } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) { - if (FindVolume(&Volume, TokenList[1])) { - if ((Volume != NULL) && (Volume->IsReadable) && (Volume->RootDir)) { - MyFreePool(SubEntry->me.Title); - SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(SubEntry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", Volume->VolName); - SubEntry->me.BadgeImage = Volume->VolBadgeImage; - SubEntry->Volume = Volume; - } // if volume is readable - } // if match found - - } else if (MyStriCmp(TokenList[0], L"initrd")) { - MyFreePool(SubEntry->InitrdPath); - SubEntry->InitrdPath = NULL; - if (TokenCount > 1) { - SubEntry->InitrdPath = StrDuplicate(TokenList[1]); - } - - } else if (MyStriCmp(TokenList[0], L"options")) { - MyFreePool(SubEntry->LoadOptions); - SubEntry->LoadOptions = NULL; - if (TokenCount > 1) { - SubEntry->LoadOptions = StrDuplicate(TokenList[1]); - } // if/else - - } else if (MyStriCmp(TokenList[0], L"add_options") && (TokenCount > 1)) { - MergeStrings(&SubEntry->LoadOptions, TokenList[1], L' '); - - } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) { - SubEntry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on"); - - } else if (MyStriCmp(TokenList[0], L"disabled")) { - SubEntry->Enabled = FALSE; - } // ief/elseif - - FreeTokenLine(&TokenList, &TokenCount); - } // while() - - if (SubEntry->InitrdPath != NULL) { - MergeStrings(&SubEntry->LoadOptions, L"initrd=", L' '); - MergeStrings(&SubEntry->LoadOptions, SubEntry->InitrdPath, 0); - MyFreePool(SubEntry->InitrdPath); - SubEntry->InitrdPath = NULL; - } // if - if (SubEntry->Enabled == TRUE) { - AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); - } - Entry->me.SubScreen = SubScreen; + REFIT_MENU_SCREEN *SubScreen; + LOADER_ENTRY *SubEntry; + UINTN TokenCount; + CHAR16 **TokenList; + + SubScreen = InitializeSubScreen(Entry); + + // Set defaults for the new entry; will be modified based on lines read from the config. file.... + SubEntry = InitializeLoaderEntry(Entry); + + if ((SubEntry == NULL) || (SubScreen == NULL)) + return; + SubEntry->me.Title = StrDuplicate(Title); + + while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) { + + if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename + MyFreePool(SubEntry->LoaderPath); + SubEntry->LoaderPath = StrDuplicate(TokenList[1]); + SubEntry->Volume = Volume; + + } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) { + if (FindVolume(&Volume, TokenList[1])) { + if ((Volume != NULL) && (Volume->IsReadable) && (Volume->RootDir)) { + MyFreePool(SubEntry->me.Title); + SubEntry->me.Title = PoolPrint(L"Boot %s from %s", + (Title != NULL) ? Title : L"Unknown", + Volume->VolName); + SubEntry->me.BadgeImage = Volume->VolBadgeImage; + SubEntry->Volume = Volume; + } // if volume is readable + } // if match found + + } else if (MyStriCmp(TokenList[0], L"initrd")) { + MyFreePool(SubEntry->InitrdPath); + SubEntry->InitrdPath = NULL; + if (TokenCount > 1) { + SubEntry->InitrdPath = StrDuplicate(TokenList[1]); + } + + } else if (MyStriCmp(TokenList[0], L"options")) { + MyFreePool(SubEntry->LoadOptions); + SubEntry->LoadOptions = NULL; + if (TokenCount > 1) { + SubEntry->LoadOptions = StrDuplicate(TokenList[1]); + } // if/else + + } else if (MyStriCmp(TokenList[0], L"add_options") && (TokenCount > 1)) { + MergeStrings(&SubEntry->LoadOptions, TokenList[1], L' '); + + } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) { + SubEntry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on"); + + } else if (MyStriCmp(TokenList[0], L"disabled")) { + SubEntry->Enabled = FALSE; + } // ief/elseif + + FreeTokenLine(&TokenList, &TokenCount); + } // while() + + if (SubEntry->InitrdPath != NULL) { + MergeStrings(&SubEntry->LoadOptions, L"initrd=", L' '); + MergeStrings(&SubEntry->LoadOptions, SubEntry->InitrdPath, 0); + MyFreePool(SubEntry->InitrdPath); + SubEntry->InitrdPath = NULL; + } // if + if (SubEntry->Enabled == TRUE) { + AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); + } + Entry->me.SubScreen = SubScreen; } // VOID AddSubmenu() // Adds the options from a SINGLE refind.conf stanza to a new loader entry and returns // that entry. The calling function is then responsible for adding the entry to the // list of entries. static LOADER_ENTRY * AddStanzaEntries(REFIT_FILE *File, REFIT_VOLUME *Volume, CHAR16 *Title) { - CHAR16 **TokenList; - UINTN TokenCount; - LOADER_ENTRY *Entry; - BOOLEAN DefaultsSet = FALSE, AddedSubmenu = FALSE; - REFIT_VOLUME *CurrentVolume = Volume; - - // prepare the menu entry - Entry = InitializeLoaderEntry(NULL); - if (Entry == NULL) - return NULL; - - Entry->Title = StrDuplicate(Title); - Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName); - Entry->me.Row = 0; - Entry->me.BadgeImage = CurrentVolume->VolBadgeImage; - Entry->Volume = CurrentVolume; - Entry->DiscoveryType = DISCOVERY_TYPE_MANUAL; - - // Parse the config file to add options for a single stanza, terminating when the token - // is "}" or when the end of file is reached. - while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) { - if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename - Entry->LoaderPath = StrDuplicate(TokenList[1]); - SetLoaderDefaults(Entry, TokenList[1], CurrentVolume); - MyFreePool(Entry->LoadOptions); - Entry->LoadOptions = NULL; // Discard default options, if any - DefaultsSet = TRUE; - - } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) { - if (FindVolume(&CurrentVolume, TokenList[1])) { - if ((CurrentVolume != NULL) && (CurrentVolume->IsReadable) && (CurrentVolume->RootDir)) { - MyFreePool(Entry->me.Title); - Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(Entry->me.Title, 255, L"Boot %s from %s", (Title != NULL) ? Title : L"Unknown", CurrentVolume->VolName); - Entry->me.BadgeImage = CurrentVolume->VolBadgeImage; - Entry->Volume = CurrentVolume; - } // if volume is readable - } // if match found - - } else if (MyStriCmp(TokenList[0], L"icon") && (TokenCount > 1)) { - MyFreePool(Entry->me.Image); - Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], GlobalConfig.IconSizes[ICON_SIZE_BIG]); - if (Entry->me.Image == NULL) { - Entry->me.Image = DummyImage(GlobalConfig.IconSizes[ICON_SIZE_BIG]); - } - - } else if (MyStriCmp(TokenList[0], L"initrd") && (TokenCount > 1)) { - MyFreePool(Entry->InitrdPath); - Entry->InitrdPath = StrDuplicate(TokenList[1]); - - } else if (MyStriCmp(TokenList[0], L"options") && (TokenCount > 1)) { - MyFreePool(Entry->LoadOptions); - Entry->LoadOptions = StrDuplicate(TokenList[1]); - - } else if (MyStriCmp(TokenList[0], L"ostype") && (TokenCount > 1)) { - if (TokenCount > 1) { - Entry->OSType = TokenList[1][0]; - } - - } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) { - Entry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on"); - - } else if (MyStriCmp(TokenList[0], L"disabled")) { - Entry->Enabled = FALSE; - - } else if (MyStriCmp(TokenList[0], L"submenuentry") && (TokenCount > 1)) { - AddSubmenu(Entry, File, CurrentVolume, TokenList[1]); - AddedSubmenu = TRUE; - - } // set options to pass to the loader program - FreeTokenLine(&TokenList, &TokenCount); - } // while() - - if (AddedSubmenu) - AddMenuEntry(Entry->me.SubScreen, &MenuEntryReturn); - - if (Entry->InitrdPath) { - MergeStrings(&Entry->LoadOptions, L"initrd=", L' '); - MergeStrings(&Entry->LoadOptions, Entry->InitrdPath, 0); - MyFreePool(Entry->InitrdPath); - Entry->InitrdPath = NULL; - } // if + CHAR16 **TokenList; + UINTN TokenCount; + LOADER_ENTRY *Entry; + BOOLEAN DefaultsSet = FALSE, AddedSubmenu = FALSE; + REFIT_VOLUME *CurrentVolume = Volume, *PreviousVolume; + + // prepare the menu entry + Entry = InitializeLoaderEntry(NULL); + if (Entry == NULL) + return NULL; + + Entry->Title = StrDuplicate(Title); + Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); + Entry->me.Title = PoolPrint(L"Boot %s from %s", + (Title != NULL) ? Title : L"Unknown", + CurrentVolume->VolName); + Entry->me.Row = 0; + Entry->me.BadgeImage = CurrentVolume->VolBadgeImage; + Entry->Volume = CurrentVolume; + Entry->DiscoveryType = DISCOVERY_TYPE_MANUAL; + + // Parse the config file to add options for a single stanza, terminating when the token + // is "}" or when the end of file is reached. + while (((TokenCount = ReadTokenLine(File, &TokenList)) > 0) && (StrCmp(TokenList[0], L"}") != 0)) { + if (MyStriCmp(TokenList[0], L"loader") && (TokenCount > 1)) { // set the boot loader filename + Entry->LoaderPath = StrDuplicate(TokenList[1]); + LOG(1, LOG_LINE_NORMAL, L"Adding manual loader for '%s'", Entry->LoaderPath); + SetLoaderDefaults(Entry, TokenList[1], CurrentVolume); + MyFreePool(Entry->LoadOptions); + Entry->LoadOptions = NULL; // Discard default options, if any + DefaultsSet = TRUE; + + } else if (MyStriCmp(TokenList[0], L"volume") && (TokenCount > 1)) { + PreviousVolume = CurrentVolume; + if (FindVolume(&CurrentVolume, TokenList[1])) { + if ((CurrentVolume != NULL) && (CurrentVolume->IsReadable) && (CurrentVolume->RootDir)) { + MyFreePool(Entry->me.Title); + Entry->me.Title = PoolPrint(L"Boot %s from %s", + (Title != NULL) ? Title : L"Unknown", + CurrentVolume->VolName); + Entry->me.BadgeImage = CurrentVolume->VolBadgeImage; + Entry->Volume = CurrentVolume; + } else { // It won't work out; reset to previous working volume.... + CurrentVolume = PreviousVolume; + } // if/else volume is readable + } // if match found + + } else if (MyStriCmp(TokenList[0], L"icon") && (TokenCount > 1)) { + MyFreePool(Entry->me.Image); + Entry->me.Image = egLoadIcon(CurrentVolume->RootDir, TokenList[1], GlobalConfig.IconSizes[ICON_SIZE_BIG]); + if (Entry->me.Image == NULL) { + Entry->me.Image = DummyImage(GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } + + } else if (MyStriCmp(TokenList[0], L"initrd") && (TokenCount > 1)) { + MyFreePool(Entry->InitrdPath); + Entry->InitrdPath = StrDuplicate(TokenList[1]); + + } else if (MyStriCmp(TokenList[0], L"options") && (TokenCount > 1)) { + MyFreePool(Entry->LoadOptions); + Entry->LoadOptions = StrDuplicate(TokenList[1]); + + } else if (MyStriCmp(TokenList[0], L"ostype") && (TokenCount > 1)) { + if (TokenCount > 1) { + Entry->OSType = TokenList[1][0]; + } - if (!DefaultsSet) - SetLoaderDefaults(Entry, L"\\EFI\\BOOT\\nemo.efi", CurrentVolume); // user included no "loader" line; use bogus one + } else if (MyStriCmp(TokenList[0], L"graphics") && (TokenCount > 1)) { + Entry->UseGraphicsMode = MyStriCmp(TokenList[1], L"on"); + + } else if (MyStriCmp(TokenList[0], L"disabled")) { + LOG(1, LOG_LINE_NORMAL, L"Entry is disabled"); + Entry->Enabled = FALSE; + + } else if (MyStriCmp(TokenList[0], L"firmware_bootnum") && (TokenCount > 1)) { + Entry->EfiBootNum = StrToHex(TokenList[1], 0, 16); + Entry->EfiLoaderPath = NULL; + Entry->LoaderPath = NULL; + MyFreePool(Entry->me.Title); + Entry->me.Title = StrDuplicate(Title); + Entry->me.BadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EFI); + Entry->me.Tag = TAG_FIRMWARE_LOADER; + + } else if (MyStriCmp(TokenList[0], L"submenuentry") && (TokenCount > 1)) { + AddSubmenu(Entry, File, CurrentVolume, TokenList[1]); + AddedSubmenu = TRUE; + + } // set options to pass to the loader program + FreeTokenLine(&TokenList, &TokenCount); + } // while() - return(Entry); + if (AddedSubmenu) + AddMenuEntry(Entry->me.SubScreen, &MenuEntryReturn); + + if (Entry->InitrdPath) { + MergeStrings(&Entry->LoadOptions, L"initrd=", L' '); + MergeStrings(&Entry->LoadOptions, Entry->InitrdPath, 0); + MyFreePool(Entry->InitrdPath); + Entry->InitrdPath = NULL; + } // if + + if (!DefaultsSet) { + // user included no "loader" line; use bogus one + SetLoaderDefaults(Entry, L"\\EFI\\BOOT\\nemo.efi", CurrentVolume); + } + + return(Entry); } // static VOID AddStanzaEntries() // Read the user-configured menu entries from refind.conf and add or delete // entries based on the contents of that file.... VOID ScanUserConfigured(CHAR16 *FileName) { - EFI_STATUS Status; - REFIT_FILE File; - REFIT_VOLUME *Volume; - CHAR16 **TokenList; - UINTN TokenCount, size; - LOADER_ENTRY *Entry; - - if (FileExists(SelfDir, FileName)) { - Status = ReadFile(SelfDir, FileName, &File, &size); - if (EFI_ERROR(Status)) - return; - - Volume = SelfVolume; - - while ((TokenCount = ReadTokenLine(&File, &TokenList)) > 0) { - if (MyStriCmp(TokenList[0], L"menuentry") && (TokenCount > 1)) { - Entry = AddStanzaEntries(&File, Volume, TokenList[1]); - if (Entry->Enabled) { - if (Entry->me.SubScreen == NULL) - GenerateSubScreen(Entry, Volume, TRUE); - AddPreparedLoaderEntry(Entry); - } else { - MyFreePool(Entry); - } // if/else + EFI_STATUS Status; + REFIT_FILE File; + REFIT_VOLUME *Volume; + CHAR16 **TokenList; + UINTN TokenCount, size; + LOADER_ENTRY *Entry; + + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for user-configured boot stanzas"); + if (FileExists(SelfDir, FileName)) { + Status = ReadFile(SelfDir, FileName, &File, &size); + if (EFI_ERROR(Status)) + return; + + Volume = SelfVolume; + + while ((TokenCount = ReadTokenLine(&File, &TokenList)) > 0) { + if (MyStriCmp(TokenList[0], L"menuentry") && (TokenCount > 1)) { + Entry = AddStanzaEntries(&File, Volume, TokenList[1]); + if ((Entry) && (Entry->Enabled)) { + if (Entry->me.SubScreen == NULL) + GenerateSubScreen(Entry, Volume, TRUE); + AddPreparedLoaderEntry(Entry); + } else { + MyFreePool(Entry); + } // if/else - } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) && - MyStriCmp(FileName, GlobalConfig.ConfigFilename)) { - if (!MyStriCmp(TokenList[1], FileName)) { - ScanUserConfigured(TokenList[1]); - } + } else if (MyStriCmp(TokenList[0], L"include") && (TokenCount == 2) && + MyStriCmp(FileName, GlobalConfig.ConfigFilename)) { + if (!MyStriCmp(TokenList[1], FileName)) { + ScanUserConfigured(TokenList[1]); + } - } // if/else if... - FreeTokenLine(&TokenList, &TokenCount); - } // while() - } // if() + } // if/else if... + FreeTokenLine(&TokenList, &TokenCount); + } // while() + } // if() } // VOID ScanUserConfigured() // Create an options file based on /etc/fstab. The resulting file has two options @@ -1019,63 +1070,65 @@ // which boots the system with "ro root={rootfs} single", where "{rootfs}" is the // filesystem identifier associated with the "/" line in /etc/fstab. static REFIT_FILE * GenerateOptionsFromEtcFstab(REFIT_VOLUME *Volume) { - UINTN TokenCount, i; - REFIT_FILE *Options = NULL, *Fstab = NULL; - EFI_STATUS Status; - CHAR16 **TokenList, *Line, Root[100]; - - if (FileExists(Volume->RootDir, L"\\etc\\fstab")) { - Options = AllocateZeroPool(sizeof(REFIT_FILE)); - Fstab = AllocateZeroPool(sizeof(REFIT_FILE)); - Status = ReadFile(Volume->RootDir, L"\\etc\\fstab", Fstab, &i); - if (CheckError(Status, L"while reading /etc/fstab")) { - if (Options != NULL) - FreePool(Options); - if (Fstab != NULL) - FreePool(Fstab); - Options = NULL; - Fstab = NULL; - } else { // File read; locate root fs and create entries - Options->Encoding = ENCODING_UTF16_LE; - while ((TokenCount = ReadTokenLine(Fstab, &TokenList)) > 0) { - if (TokenCount > 2) { - Root[0] = '\0'; - if (StrCmp(TokenList[1], L"\\") == 0) { - SPrint(Root, 99, L"%s", TokenList[0]); - } else if (StrCmp(TokenList[2], L"\\") == 0) { - SPrint(Root, 99, L"%s=%s", TokenList[0], TokenList[1]); - } // if/elseif/elseif - if (Root[0] != L'\0') { - for (i = 0; i < StrLen(Root); i++) - if (Root[i] == '\\') - Root[i] = '/'; - Line = PoolPrint(L"\"Boot with normal options\" \"ro root=%s\"\n", Root); - MergeStrings((CHAR16 **) &(Options->Buffer), Line, 0); - MyFreePool(Line); - Line = PoolPrint(L"\"Boot into single-user mode\" \"ro root=%s single\"\n", Root); - MergeStrings((CHAR16**) &(Options->Buffer), Line, 0); - MyFreePool(Line); - Options->BufferSize = StrLen((CHAR16*) Options->Buffer) * sizeof(CHAR16); - } // if - } // if - FreeTokenLine(&TokenList, &TokenCount); - } // while + UINTN TokenCount, i; + REFIT_FILE *Options = NULL, *Fstab = NULL; + EFI_STATUS Status; + CHAR16 **TokenList, *Line, *Root = NULL; - if (Options->Buffer) { - Options->Current8Ptr = (CHAR8 *)Options->Buffer; - Options->End8Ptr = Options->Current8Ptr + Options->BufferSize; - Options->Current16Ptr = (CHAR16 *)Options->Buffer; - Options->End16Ptr = Options->Current16Ptr + (Options->BufferSize >> 1); - } else { - MyFreePool(Options); - Options = NULL; - } - - MyFreePool(Fstab->Buffer); - MyFreePool(Fstab); - } // if/else file read error - } // if /etc/fstab exists - return Options; + if (FileExists(Volume->RootDir, L"\\etc\\fstab")) { + Options = AllocateZeroPool(sizeof(REFIT_FILE)); + Fstab = AllocateZeroPool(sizeof(REFIT_FILE)); + Status = ReadFile(Volume->RootDir, L"\\etc\\fstab", Fstab, &i); + if (CheckError(Status, L"while reading /etc/fstab")) { + if (Options != NULL) + FreePool(Options); + if (Fstab != NULL) + FreePool(Fstab); + Options = NULL; + Fstab = NULL; + } else { // File read; locate root fs and create entries + Options->Encoding = ENCODING_UTF16_LE; + while ((TokenCount = ReadTokenLine(Fstab, &TokenList)) > 0) { + LOG(3, LOG_LINE_NORMAL, L"Read line from /etc/fstab holding %d tokens", TokenCount); + if (TokenCount > 2) { + if (StrCmp(TokenList[1], L"\\") == 0) { + Root = PoolPrint(L"%s", TokenList[0]); + } else if (StrCmp(TokenList[2], L"\\") == 0) { + Root = PoolPrint(L"%s=%s", TokenList[0], TokenList[1]); + } // if/elseif/elseif + if (Root && (Root[0] != L'\0')) { + for (i = 0; i < StrLen(Root); i++) + if (Root[i] == '\\') + Root[i] = '/'; + Line = PoolPrint(L"\"Boot with normal options\" \"ro root=%s\"\n", Root); + MergeStrings((CHAR16 **) &(Options->Buffer), Line, 0); + MyFreePool(Line); + Line = PoolPrint(L"\"Boot into single-user mode\" \"ro root=%s single\"\n", Root); + MergeStrings((CHAR16**) &(Options->Buffer), Line, 0); + MyFreePool(Line); + Options->BufferSize = StrLen((CHAR16*) Options->Buffer) * sizeof(CHAR16); + } // if + MyFreePool(Root); + Root = NULL; + } // if + FreeTokenLine(&TokenList, &TokenCount); + } // while + + if (Options->Buffer) { + Options->Current8Ptr = (CHAR8 *)Options->Buffer; + Options->End8Ptr = Options->Current8Ptr + Options->BufferSize; + Options->Current16Ptr = (CHAR16 *)Options->Buffer; + Options->End16Ptr = Options->Current16Ptr + (Options->BufferSize >> 1); + } else { + MyFreePool(Options); + Options = NULL; + } + + MyFreePool(Fstab->Buffer); + MyFreePool(Fstab); + } // if/else file read error + } // if /etc/fstab exists + return Options; } // GenerateOptionsFromEtcFstab() // Create options from partition type codes. Specifically, if the earlier @@ -1099,10 +1152,12 @@ WriteStatus = GlobalConfig.DiscoveredRoot->IsMarkedReadOnly ? L"ro" : L"rw"; ToLower(GuidString); if (GuidString) { - Line = PoolPrint(L"\"Boot with normal options\" \"%s root=/dev/disk/by-partuuid/%s\"\n", WriteStatus, GuidString); + Line = PoolPrint(L"\"Boot with normal options\" \"%s root=/dev/disk/by-partuuid/%s\"\n", + WriteStatus, GuidString); MergeStrings((CHAR16 **) &(Options->Buffer), Line, 0); MyFreePool(Line); - Line = PoolPrint(L"\"Boot into single-user mode\" \"%s root=/dev/disk/by-partuuid/%s single\"\n", WriteStatus, GuidString); + Line = PoolPrint(L"\"Boot into single-user mode\" \"%s root=/dev/disk/by-partuuid/%s single\"\n", + WriteStatus, GuidString); MergeStrings((CHAR16**) &(Options->Buffer), Line, 0); MyFreePool(Line); MyFreePool(GuidString); @@ -1130,61 +1185,61 @@ // The return value is a pointer to the REFIT_FILE handle for the file, or NULL if // it wasn't found. REFIT_FILE * ReadLinuxOptionsFile(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { - CHAR16 *OptionsFilename, *FullFilename; - BOOLEAN GoOn = TRUE, FileFound = FALSE; - UINTN i = 0, size; - REFIT_FILE *File = NULL; - EFI_STATUS Status; - - do { - OptionsFilename = FindCommaDelimited(LINUX_OPTIONS_FILENAMES, i++); - FullFilename = FindPath(LoaderPath); - if ((OptionsFilename != NULL) && (FullFilename != NULL)) { - MergeStrings(&FullFilename, OptionsFilename, '\\'); - if (FileExists(Volume->RootDir, FullFilename)) { - File = AllocateZeroPool(sizeof(REFIT_FILE)); - Status = ReadFile(Volume->RootDir, FullFilename, File, &size); - if (CheckError(Status, L"while loading the Linux options file")) { - if (File != NULL) - FreePool(File); - File = NULL; - } else { - GoOn = FALSE; - FileFound = TRUE; - } // if/else error - } // if file exists - } else { // a filename string is NULL - GoOn = FALSE; - } // if/else - MyFreePool(OptionsFilename); - MyFreePool(FullFilename); - OptionsFilename = FullFilename = NULL; - } while (GoOn); - if (!FileFound) { - // No refind_linux.conf file; look for /etc/fstab and try to pull values from there.... - File = GenerateOptionsFromEtcFstab(Volume); - // If still no joy, try to use Freedesktop.org Discoverable Partitions Spec.... - if (!File) - File = GenerateOptionsFromPartTypes(); - } // if - return (File); + CHAR16 *OptionsFilename, *FullFilename; + BOOLEAN GoOn = TRUE, FileFound = FALSE; + UINTN i = 0, size; + REFIT_FILE *File = NULL; + EFI_STATUS Status; + + do { + OptionsFilename = FindCommaDelimited(LINUX_OPTIONS_FILENAMES, i++); + FullFilename = FindPath(LoaderPath); + if ((OptionsFilename != NULL) && (FullFilename != NULL)) { + MergeStrings(&FullFilename, OptionsFilename, '\\'); + if (FileExists(Volume->RootDir, FullFilename)) { + File = AllocateZeroPool(sizeof(REFIT_FILE)); + Status = ReadFile(Volume->RootDir, FullFilename, File, &size); + if (CheckError(Status, L"while loading the Linux options file")) { + if (File != NULL) + FreePool(File); + File = NULL; + } else { + GoOn = FALSE; + FileFound = TRUE; + } // if/else error + } // if file exists + } else { // a filename string is NULL + GoOn = FALSE; + } // if/else + MyFreePool(OptionsFilename); + MyFreePool(FullFilename); + OptionsFilename = FullFilename = NULL; + } while (GoOn); + if (!FileFound) { + // No refind_linux.conf file; look for /etc/fstab and try to pull values from there.... + File = GenerateOptionsFromEtcFstab(Volume); + // If still no joy, try to use Freedesktop.org Discoverable Partitions Spec.... + if (!File) + File = GenerateOptionsFromPartTypes(); + } // if + return (File); } // static REFIT_FILE * ReadLinuxOptionsFile() // Retrieve a single line of options from a Linux kernel options file CHAR16 * GetFirstOptionsFromFile(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) { - UINTN TokenCount; - CHAR16 *Options = NULL; - CHAR16 **TokenList; - REFIT_FILE *File; - - File = ReadLinuxOptionsFile(LoaderPath, Volume); - if (File != NULL) { - TokenCount = ReadTokenLine(File, &TokenList); - if (TokenCount > 1) - Options = StrDuplicate(TokenList[1]); - FreeTokenLine(&TokenList, &TokenCount); - FreePool(File); - } // if - return Options; + UINTN TokenCount; + CHAR16 *Options = NULL; + CHAR16 **TokenList; + REFIT_FILE *File; + + File = ReadLinuxOptionsFile(LoaderPath, Volume); + if (File != NULL) { + TokenCount = ReadTokenLine(File, &TokenList); + if (TokenCount > 1) + Options = StrDuplicate(TokenList[1]); + FreeTokenLine(&TokenList, &TokenCount); + FreePool(File); + } // if + return Options; } // static CHAR16 * GetOptionsFile() diff -Nru refind-0.12.0/refind/driver_support.c refind-0.13.2/refind/driver_support.c --- refind-0.12.0/refind/driver_support.c 2020-03-07 18:55:34.000000000 +0000 +++ refind-0.13.2/refind/driver_support.c 2021-03-08 03:50:07.000000000 +0000 @@ -74,6 +74,7 @@ #include "mystrings.h" #include "screen.h" #include "launch_efi.h" +#include "log.h" #include "../include/refit_call_wrapper.h" #if defined (EFIX64) @@ -543,7 +544,7 @@ REFIT_DIR_ITER DirIter; UINTN NumFound = 0; EFI_FILE_INFO *DirEntry; - CHAR16 FileName[256]; + CHAR16 *FileName; CleanUpPathNameSlashes(Path); // look through contents of the directory @@ -552,15 +553,17 @@ if (DirEntry->FileName[0] == '.') continue; // skip this - SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName); + FileName = PoolPrint(L"%s\\%s", Path, DirEntry->FileName); NumFound++; Status = StartEFIImage(SelfVolume, FileName, L"", DirEntry->FileName, 0, FALSE, TRUE); MyFreePool(DirEntry); + MyFreePool(FileName); } // while Status = DirIterClose(&DirIter); if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) { - SPrint(FileName, 255, L"while scanning the %s directory", Path); + FileName = PoolPrint(L"while scanning the %s directory", Path); CheckError(Status, FileName); + MyFreePool(FileName); } return (NumFound); } // static UINTN ScanDriverDir() @@ -575,6 +578,7 @@ CHAR16 *Directory, *SelfDirectory; UINTN i = 0, Length, NumFound = 0; + LOG(1, LOG_LINE_SEPARATOR, L"Loading drivers"); // load drivers from the subdirectories of rEFInd's home directory specified // in the DRIVER_DIRS constant. while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) { diff -Nru refind-0.12.0/refind/global.h refind-0.13.2/refind/global.h --- refind-0.12.0/refind/global.h 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/refind/global.h 2021-03-13 18:04:07.000000000 +0000 @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 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 @@ -64,25 +64,26 @@ #define TAG_TOOL (4) #define TAG_LOADER (5) #define TAG_LEGACY (6) -#define TAG_EXIT (7) -#define TAG_SHELL (8) -#define TAG_GPTSYNC (9) -#define TAG_LEGACY_UEFI (10) -#define TAG_APPLE_RECOVERY (11) -#define TAG_WINDOWS_RECOVERY (12) -#define TAG_MOK_TOOL (13) -#define TAG_FIRMWARE (14) -#define TAG_MEMTEST (15) -#define TAG_GDISK (16) -#define TAG_NETBOOT (17) -#define TAG_CSR_ROTATE (18) -#define TAG_FWUPDATE_TOOL (19) -#define TAG_HIDDEN (20) -#define TAG_INSTALL (21) -#define TAG_BOOTORDER (22) -#define NUM_TOOLS (23) +#define TAG_FIRMWARE_LOADER (7) +#define TAG_EXIT (8) +#define TAG_SHELL (9) +#define TAG_GPTSYNC (10) +#define TAG_LEGACY_UEFI (11) +#define TAG_APPLE_RECOVERY (12) +#define TAG_WINDOWS_RECOVERY (13) +#define TAG_MOK_TOOL (14) +#define TAG_FIRMWARE (15) +#define TAG_MEMTEST (16) +#define TAG_GDISK (17) +#define TAG_NETBOOT (18) +#define TAG_CSR_ROTATE (19) +#define TAG_FWUPDATE_TOOL (20) +#define TAG_HIDDEN (21) +#define TAG_INSTALL (22) +#define TAG_BOOTORDER (23) +#define NUM_TOOLS (24) -#define NUM_SCAN_OPTIONS 10 +#define NUM_SCAN_OPTIONS 11 #define DEFAULT_ICONS_DIR L"icons" @@ -138,8 +139,9 @@ #define FS_TYPE_REISERFS 9 #define FS_TYPE_BTRFS 10 #define FS_TYPE_XFS 11 -#define FS_TYPE_ISO9660 12 -#define NUM_FS_TYPES 13 +#define FS_TYPE_JFS 12 +#define FS_TYPE_ISO9660 13 +#define NUM_FS_TYPES 14 // How to scale banner images #define BANNER_NOSCALE 0 @@ -221,9 +223,12 @@ #define NULL_GUID_VALUE { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; #define REFIND_GUID_VALUE { 0x36D08FA7, 0xCF0B, 0x42F5, {0x8F, 0x14, 0x68, 0xDF, 0x73, 0xED, 0x37, 0x40} }; #define ESP_GUID_VALUE { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; +#define SYSTEMD_GUID_VALUE { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f }}; + // Configuration file variables #define KERNEL_VERSION L"%v" +#define MAX_RES_CODE 2147483647 /* 2^31 - 1 */ // // global definitions @@ -253,8 +258,9 @@ EFI_DEVICE_PATH *DevicePath; EFI_HANDLE DeviceHandle; EFI_FILE *RootDir; - CHAR16 *VolName; - CHAR16 *PartName; + CHAR16 *PartName; // GPT partition name + CHAR16 *FsName; // Filesystem name + CHAR16 *VolName; // One of the two above OR fs description (e.g., "2 GiB FAT volume") EFI_GUID VolUuid; EFI_GUID PartGuid; EFI_GUID PartTypeGuid; @@ -288,7 +294,7 @@ } REFIT_MENU_ENTRY; typedef struct _refit_menu_screen { - CHAR16 *Title; + CHAR16 *Title; // For EFI firmware entry, this includes "Reboot to" prefix EG_IMAGE *TitleImage; UINTN InfoLineCount; CHAR16 **InfoLines; @@ -302,7 +308,7 @@ typedef struct { REFIT_MENU_ENTRY me; - CHAR16 *Title; + CHAR16 *Title; // For EFI firmware entry, this is "raw" title CHAR16 *LoaderPath; REFIT_VOLUME *Volume; BOOLEAN UseGraphicsMode; @@ -311,6 +317,8 @@ CHAR16 *InitrdPath; // Linux stub loader only CHAR8 OSType; UINTN DiscoveryType; + EFI_DEVICE_PATH *EfiLoaderPath; // path to NVRAM-defined loader + UINT16 EfiBootNum; // Boot#### number for NVRAM-defined loader } LOADER_ENTRY; typedef struct { @@ -333,6 +341,7 @@ BOOLEAN UseNvram; BOOLEAN ShutdownAfterTimeout; BOOLEAN Install; + BOOLEAN WriteSystemdVars; UINTN RequestedScreenWidth; UINTN RequestedScreenHeight; UINTN BannerBottomEdge; @@ -347,6 +356,7 @@ UINTN MouseSpeed; UINTN IconSizes[4]; UINTN BannerScale; + UINTN LogLevel; REFIT_VOLUME *DiscoveredRoot; EFI_DEVICE_PATH *SelfDevicePath; CHAR16 *BannerFileName; @@ -360,6 +370,7 @@ CHAR16 *DontScanDirs; CHAR16 *DontScanFiles; CHAR16 *DontScanTools; + CHAR16 *DontScanFirmware; CHAR16 *WindowsRecoveryFiles; CHAR16 *MacOSRecoveryFiles; CHAR16 *DriverDirs; @@ -396,6 +407,8 @@ extern REFIT_MENU_SCREEN MainMenu; extern REFIT_MENU_ENTRY MenuEntryReturn; +// Global function definitions.... + VOID AboutrEFInd(VOID); EG_IMAGE * GetDiskBadge(IN UINTN DiskType); LOADER_ENTRY * MakeGenericLoaderEntry(VOID); diff -Nru refind-0.12.0/refind/gpt.c refind-0.13.2/refind/gpt.c --- refind-0.12.0/refind/gpt.c 2020-02-20 14:37:44.000000000 +0000 +++ refind-0.13.2/refind/gpt.c 2021-03-07 18:29:24.000000000 +0000 @@ -36,34 +36,34 @@ // allocate memory for the Entries data structure, since its size is variable // and is determined by the contents of Header. GPT_DATA * AllocateGptData(VOID) { - GPT_DATA *GptData; + GPT_DATA *GptData; - GptData = AllocateZeroPool(sizeof(GPT_DATA)); - if (GptData != NULL) { - GptData->ProtectiveMBR = AllocateZeroPool(sizeof(MBR_RECORD)); - GptData->Header = AllocateZeroPool(sizeof(GPT_HEADER)); - if ((GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) { - MyFreePool(GptData->ProtectiveMBR); - MyFreePool(GptData->Header); - MyFreePool(GptData); - GptData = NULL; - } // if - } // if - return GptData; + GptData = AllocateZeroPool(sizeof(GPT_DATA)); + if (GptData != NULL) { + GptData->ProtectiveMBR = AllocateZeroPool(sizeof(MBR_RECORD)); + GptData->Header = AllocateZeroPool(sizeof(GPT_HEADER)); + if ((GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) { + MyFreePool(GptData->ProtectiveMBR); + MyFreePool(GptData->Header); + MyFreePool(GptData); + GptData = NULL; + } // if + } // if + return GptData; } // GPT_DATA * AllocateGptData() // Unallocate a single GPT_DATA structure. This does NOT follow the // linked list, though. VOID ClearGptData(GPT_DATA *Data) { - if (Data) { - if (Data->ProtectiveMBR) - MyFreePool(Data->ProtectiveMBR); - if (Data->Header) - MyFreePool(Data->Header); - if (Data->Entries) - MyFreePool(Data->Entries); - MyFreePool(Data); - } // if + if (Data) { + if (Data->ProtectiveMBR) + MyFreePool(Data->ProtectiveMBR); + if (Data->Header) + MyFreePool(Data->Header); + if (Data->Entries) + MyFreePool(Data->Entries); + MyFreePool(Data); + } // if } // VOID ClearGptData() // TODO: Make this work on big-endian systems; at the moment, it contains @@ -71,36 +71,36 @@ // Returns TRUE if the GPT protective MBR and header data appear valid, // FALSE otherwise. static BOOLEAN GptHeaderValid(GPT_DATA *GptData) { - BOOLEAN IsValid; - UINT32 CrcValue, StoredCrcValue; - UINTN HeaderSize = sizeof(GPT_HEADER); - - if ((GptData == NULL) || (GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) - return FALSE; - - IsValid = (GptData->ProtectiveMBR->MBRSignature == 0xAA55); - IsValid = IsValid && ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) || - (GptData->ProtectiveMBR->partitions[1].type == 0xEE) || - (GptData->ProtectiveMBR->partitions[2].type == 0xEE) || - (GptData->ProtectiveMBR->partitions[3].type == 0xEE)); - - IsValid = IsValid && ((GptData->Header->signature == 0x5452415020494645ULL) && - (GptData->Header->spec_revision == 0x00010000) && - (GptData->Header->entry_size == 128)); - - // Looks good so far; check CRC value.... - if (IsValid) { - if (GptData->Header->header_size < HeaderSize) - HeaderSize = GptData->Header->header_size; - StoredCrcValue = GptData->Header->header_crc32; - GptData->Header->header_crc32 = 0; - CrcValue = crc32(0x0, GptData->Header, HeaderSize); - if (CrcValue != StoredCrcValue) - IsValid = FALSE; - GptData->Header->header_crc32 = StoredCrcValue; - } // if + BOOLEAN IsValid; + UINT32 CrcValue, StoredCrcValue; + UINTN HeaderSize = sizeof(GPT_HEADER); + + if ((GptData == NULL) || (GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL)) + return FALSE; + + IsValid = (GptData->ProtectiveMBR->MBRSignature == 0xAA55); + IsValid = IsValid && ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) || + (GptData->ProtectiveMBR->partitions[1].type == 0xEE) || + (GptData->ProtectiveMBR->partitions[2].type == 0xEE) || + (GptData->ProtectiveMBR->partitions[3].type == 0xEE)); + + IsValid = IsValid && ((GptData->Header->signature == 0x5452415020494645ULL) && + (GptData->Header->spec_revision == 0x00010000) && + (GptData->Header->entry_size == 128)); + + // Looks good so far; check CRC value.... + if (IsValid) { + if (GptData->Header->header_size < HeaderSize) + HeaderSize = GptData->Header->header_size; + StoredCrcValue = GptData->Header->header_crc32; + GptData->Header->header_crc32 = 0; + CrcValue = crc32(0x0, GptData->Header, HeaderSize); + if (CrcValue != StoredCrcValue) + IsValid = FALSE; + GptData->Header->header_crc32 = StoredCrcValue; + } // if - return IsValid; + return IsValid; } // BOOLEAN GptHeaderValid() // Read GPT data from Volume and store it in *Data. Note that this function @@ -115,145 +115,152 @@ // non-critical data, so it's OK to return nothing, but having the program // hang on reading garbage or return nonsense could be very bad. EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) { - EFI_STATUS Status = EFI_SUCCESS; - UINT64 BufferSize; - UINTN i; - GPT_DATA *GptData = NULL; // Temporary holding storage; transferred to *Data later - - if ((Volume == NULL) || (Data == NULL)) - return EFI_INVALID_PARAMETER; - - // get block i/o - if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) { - Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO)); - if (EFI_ERROR(Status)) { - Volume->BlockIO = NULL; - Print(L"Warning: Can't get BlockIO protocol in ReadGptData().\n"); - Status = EFI_NOT_READY; - } - } // if - - if ((Status == EFI_SUCCESS) && ((!Volume->BlockIO->Media->MediaPresent) || (Volume->BlockIO->Media->LogicalPartition))) - Status = EFI_NO_MEDIA; - - if (Status == EFI_SUCCESS) { - GptData = AllocateGptData(); // Note: All but GptData->Entries - if (GptData == NULL) { - Status = EFI_OUT_OF_RESOURCES; - } // if - } // if - - // Read the MBR and store it in GptData->ProtectiveMBR. - if (Status == EFI_SUCCESS) { - Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, - 0, sizeof(MBR_RECORD), (VOID*) GptData->ProtectiveMBR); - } - - // Read the GPT header and store it in GptData->Header. - if (Status == EFI_SUCCESS) { - Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, - 1, sizeof(GPT_HEADER), GptData->Header); - } - - // If it looks like a valid protective MBR & GPT header, try to do more with it.... - if (Status == EFI_SUCCESS) { - if (GptHeaderValid(GptData)) { - // Load actual GPT table.... - BufferSize = GptData->Header->entry_count * 128; - GptData->Entries = AllocatePool(BufferSize); - if (GptData->Entries == NULL) + EFI_STATUS Status = EFI_SUCCESS; + UINT64 BufferSize; + UINTN i; + GPT_DATA *GptData = NULL; // Temporary holding storage; transferred to *Data later + + if ((Volume == NULL) || (Data == NULL)) + return EFI_INVALID_PARAMETER; + + // get block i/o + if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) { + Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, + &BlockIoProtocol, (VOID **) &(Volume->BlockIO)); + if (EFI_ERROR(Status)) { + Volume->BlockIO = NULL; + Print(L"Warning: Can't get BlockIO protocol in ReadGptData().\n"); + Status = EFI_NOT_READY; + } + } // if + + if ((Status == EFI_SUCCESS) && ((!Volume->BlockIO->Media->MediaPresent) || + (Volume->BlockIO->Media->LogicalPartition))) + Status = EFI_NO_MEDIA; + + if (Status == EFI_SUCCESS) { + GptData = AllocateGptData(); // Note: All but GptData->Entries + if (GptData == NULL) { Status = EFI_OUT_OF_RESOURCES; + } // if + } // if - if (Status == EFI_SUCCESS) - Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, - GptData->Header->entry_lba, BufferSize, GptData->Entries); - - // Check CRC status of table - if ((Status == EFI_SUCCESS) && (crc32(0x0, GptData->Entries, BufferSize) != GptData->Header->entry_crc32)) - Status = EFI_CRC_ERROR; - - // Now, ensure that every name is null-terminated.... - if (Status == EFI_SUCCESS) { - for (i = 0; i < GptData->Header->entry_count; i++) - GptData->Entries[i].name[35] = '\0'; - } // if - } else { - Status = EFI_UNSUPPORTED; - } // if/else valid header - } // if header read OK - - if (Status == EFI_SUCCESS) { - // Everything looks OK, so copy it over - ClearGptData(*Data); - *Data = GptData; - } else { - ClearGptData(GptData); - } // if/else + // Read the MBR and store it in GptData->ProtectiveMBR. + if (Status == EFI_SUCCESS) { + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, + Volume->BlockIO->Media->MediaId, 0, + sizeof(MBR_RECORD), (VOID*) GptData->ProtectiveMBR); + } + + // Read the GPT header and store it in GptData->Header. + if (Status == EFI_SUCCESS) { + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, + Volume->BlockIO->Media->MediaId, 1, + sizeof(GPT_HEADER), GptData->Header); + } + + // If it looks like a valid protective MBR & GPT header, try to do more with it.... + if (Status == EFI_SUCCESS) { + if (GptHeaderValid(GptData)) { + // Load actual GPT table.... + BufferSize = GptData->Header->entry_count * 128; + GptData->Entries = AllocatePool(BufferSize); + if (GptData->Entries == NULL) + Status = EFI_OUT_OF_RESOURCES; + + if (Status == EFI_SUCCESS) + Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, + Volume->BlockIO->Media->MediaId, + GptData->Header->entry_lba, BufferSize, + GptData->Entries); + + // Check CRC status of table + if ((Status == EFI_SUCCESS) && + (crc32(0x0, GptData->Entries, BufferSize) != GptData->Header->entry_crc32)) + Status = EFI_CRC_ERROR; + + // Now, ensure that every name is null-terminated.... + if (Status == EFI_SUCCESS) { + for (i = 0; i < GptData->Header->entry_count; i++) + GptData->Entries[i].name[35] = '\0'; + } // if + } else { + Status = EFI_UNSUPPORTED; + } // if/else valid header + } // if header read OK + + if (Status == EFI_SUCCESS) { + // Everything looks OK, so copy it over + ClearGptData(*Data); + *Data = GptData; + } else { + ClearGptData(GptData); + } // if/else - return Status; + return Status; } // EFI_STATUS ReadGptData() // Look in gPartitions for a partition with the specified Guid. If found, return // a pointer to that partition's data. If not found, return a NULL pointer. // The calling function is responsible for freeing the returned memory. GPT_ENTRY * FindPartWithGuid(EFI_GUID *Guid) { - UINTN i; - GPT_ENTRY *Found = NULL; - GPT_DATA *GptData; - - if ((Guid == NULL) || (gPartitions == NULL)) - return NULL; - - GptData = gPartitions; - while ((GptData != NULL) && (!Found)) { - i = 0; - while ((i < GptData->Header->entry_count) && (!Found)) { - if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid)) { - Found = AllocateZeroPool(sizeof(GPT_ENTRY)); - CopyMem(Found, &GptData->Entries[i], sizeof(GPT_ENTRY)); - } else { - i++; - } // if/else - } // while(scanning entries) - GptData = GptData->NextEntry; - } // while(scanning GPTs) - return Found; + UINTN i; + GPT_ENTRY *Found = NULL; + GPT_DATA *GptData; + + if ((Guid == NULL) || (gPartitions == NULL)) + return NULL; + + GptData = gPartitions; + while ((GptData != NULL) && (!Found)) { + i = 0; + while ((i < GptData->Header->entry_count) && (!Found)) { + if (GuidsAreEqual((EFI_GUID*) &(GptData->Entries[i].partition_guid), Guid)) { + Found = AllocateZeroPool(sizeof(GPT_ENTRY)); + CopyMem(Found, &GptData->Entries[i], sizeof(GPT_ENTRY)); + } else { + i++; + } // if/else + } // while(scanning entries) + GptData = GptData->NextEntry; + } // while(scanning GPTs) + return Found; } // GPT_ENTRY * FindPartWithGuid() // Erase the gPartitions linked-list data structure VOID ForgetPartitionTables(VOID) { - GPT_DATA *Next; + GPT_DATA *Next; - while (gPartitions != NULL) { - Next = gPartitions->NextEntry; - ClearGptData(gPartitions); - gPartitions = Next; - } // while + while (gPartitions != NULL) { + Next = gPartitions->NextEntry; + ClearGptData(gPartitions); + gPartitions = Next; + } // while } // VOID ForgetPartitionTables() // If Volume points to a whole disk with a GPT, add it to the gPartitions // linked list of GPTs. VOID AddPartitionTable(REFIT_VOLUME *Volume) { - GPT_DATA *GptData = NULL, *GptList; - EFI_STATUS Status; - UINTN NumTables = 1; - - Status = ReadGptData(Volume, &GptData); - if (Status == EFI_SUCCESS) { - if (gPartitions == NULL) { - gPartitions = GptData; - } else { - GptList = gPartitions; - while (GptList->NextEntry != NULL) { - GptList = GptList->NextEntry; + GPT_DATA *GptData = NULL, *GptList; + EFI_STATUS Status; + UINTN NumTables = 1; + + Status = ReadGptData(Volume, &GptData); + if (Status == EFI_SUCCESS) { + if (gPartitions == NULL) { + gPartitions = GptData; + } else { + GptList = gPartitions; + while (GptList->NextEntry != NULL) { + GptList = GptList->NextEntry; + NumTables++; + } // while + GptList->NextEntry = GptData; NumTables++; - } // while - GptList->NextEntry = GptData; - NumTables++; - } // if/else - } else if (GptData != NULL) { - ClearGptData(GptData); - NumTables = 0; - } // if/else + } // if/else + } else if (GptData != NULL) { + ClearGptData(GptData); + NumTables = 0; + } // if/else } // VOID AddPartitionTable() diff -Nru refind-0.12.0/refind/icns.c refind-0.13.2/refind/icns.c --- refind-0.12.0/refind/icns.c 2020-03-07 19:00:01.000000000 +0000 +++ refind-0.13.2/refind/icns.c 2021-03-08 03:50:07.000000000 +0000 @@ -37,6 +37,7 @@ #include "global.h" #include "lib.h" #include "icns.h" +#include "log.h" #include "config.h" #include "mystrings.h" #include "../refind/screen.h" @@ -74,6 +75,7 @@ { NULL, L"vol_external", ICON_SIZE_BADGE }, { NULL, L"vol_optical", ICON_SIZE_BADGE }, { NULL, L"vol_net", ICON_SIZE_BADGE }, + { NULL, L"vol_efi", ICON_SIZE_BADGE }, { NULL, L"mouse", ICON_SIZE_MOUSE } }; @@ -102,34 +104,43 @@ EG_IMAGE * LoadOSIcon(IN CHAR16 *OSIconName OPTIONAL, IN CHAR16 *FallbackIconName, BOOLEAN BootLogo) { EG_IMAGE *Image = NULL; - CHAR16 *CutoutName, BaseName[256]; + CHAR16 *CutoutName, *BaseName; UINTN Index = 0; + LOG(4, LOG_LINE_NORMAL, L"Entering LoadOSIcon()"); if (GlobalConfig.TextOnly) // skip loading if it's not used anyway return NULL; + LOG(4, LOG_LINE_NORMAL, L"Trying to find an icon from '%s'", OSIconName); // First, try to find an icon from the OSIconName list.... while (((CutoutName = FindCommaDelimited(OSIconName, Index++)) != NULL) && (Image == NULL)) { - SPrint(BaseName, 255, L"%s_%s", BootLogo ? L"boot" : L"os", CutoutName); + BaseName = PoolPrint(L"%s_%s", BootLogo ? L"boot" : L"os", CutoutName); Image = egFindIcon(BaseName, GlobalConfig.IconSizes[ICON_SIZE_BIG]); MyFreePool(CutoutName); + MyFreePool(BaseName); } // If that fails, try again using the FallbackIconName.... if (Image == NULL) { - SPrint(BaseName, 255, L"%s_%s", BootLogo ? L"boot" : L"os", FallbackIconName); + BaseName = PoolPrint(L"%s_%s", BootLogo ? L"boot" : L"os", FallbackIconName); + LOG(4, LOG_LINE_NORMAL, L"Trying to find an icon from '%s'", BaseName); Image = egFindIcon(BaseName, GlobalConfig.IconSizes[ICON_SIZE_BIG]); + MyFreePool(BaseName); } // If that fails and if BootLogo was set, try again using the "os_" start of the name.... if (BootLogo && (Image == NULL)) { - SPrint(BaseName, 255, L"os_%s", FallbackIconName); + BaseName = PoolPrint(L"os_%s", FallbackIconName); + LOG(4, LOG_LINE_NORMAL, L"Trying to find an icon from '%s'", BaseName); Image = egFindIcon(BaseName, GlobalConfig.IconSizes[ICON_SIZE_BIG]); + MyFreePool(BaseName); } // If all of these fail, return the dummy image.... - if (Image == NULL) + if (Image == NULL) { + LOG(4, LOG_LINE_NORMAL, L"Setting dummy image"); Image = DummyImage(GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } return Image; } /* EG_IMAGE * LoadOSIcon() */ diff -Nru refind-0.12.0/refind/icns.h refind-0.13.2/refind/icns.h --- refind-0.12.0/refind/icns.h 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/refind/icns.h 2021-02-21 21:40:58.000000000 +0000 @@ -77,8 +77,9 @@ #define BUILTIN_ICON_VOL_EXTERNAL (19) #define BUILTIN_ICON_VOL_OPTICAL (20) #define BUILTIN_ICON_VOL_NET (21) -#define BUILTIN_ICON_MOUSE (22) -#define BUILTIN_ICON_COUNT (23) +#define BUILTIN_ICON_VOL_EFI (22) +#define BUILTIN_ICON_MOUSE (23) +#define BUILTIN_ICON_COUNT (24) #endif diff -Nru refind-0.12.0/refind/install.c refind-0.13.2/refind/install.c --- refind-0.12.0/refind/install.c 2020-03-09 18:07:31.000000000 +0000 +++ refind-0.13.2/refind/install.c 2021-03-08 03:49:57.000000000 +0000 @@ -14,6 +14,7 @@ #include "lib.h" #include "screen.h" #include "install.h" +#include "log.h" #include "menu.h" #include "mystrings.h" #include "../include/refit_call_wrapper.h" @@ -36,6 +37,7 @@ static VOID DeleteESPList(ESP_LIST *AllESPs) { ESP_LIST *Temp; + LOG(3, LOG_LINE_NORMAL, L"Deleting list of ESPs"); while (AllESPs != NULL) { Temp = AllESPs; AllESPs = AllESPs->NextESP; @@ -53,6 +55,7 @@ UINTN VolumeIndex; EFI_GUID ESPGuid = ESP_GUID_VALUE; + LOG(2, LOG_LINE_NORMAL, L"Searching for ESPs"); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL && GuidsAreEqual(&(Volumes[VolumeIndex]->PartTypeGuid), &ESPGuid) && @@ -85,13 +88,15 @@ static REFIT_VOLUME *PickOneESP(ESP_LIST *AllESPs) { ESP_LIST *CurrentESP; REFIT_VOLUME *ChosenVolume = NULL; - CHAR16 *Temp = NULL, *GuidStr, *PartName, *VolName; + CHAR16 *Temp = NULL, *GuidStr, *PartName, *FsName, *VolName; INTN DefaultEntry = 0, MenuExit = MENU_EXIT_ESCAPE, i = 1; MENU_STYLE_FUNC Style = TextMenuStyle; REFIT_MENU_ENTRY *ChosenOption, *MenuEntryItem = NULL; REFIT_MENU_SCREEN InstallMenu = { L"Install rEFInd", NULL, 0, NULL, 0, NULL, 0, NULL, L"Select a destination and press Enter or", L"press Esc to return to main menu without changes" }; + + LOG(2, LOG_LINE_NORMAL, L"Prompting user to select an ESP for installation"); if (AllowGraphicsMode) Style = GraphicsMenuStyle; @@ -102,15 +107,21 @@ MenuEntryItem = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY)); GuidStr = GuidAsString(&(CurrentESP->Volume->PartGuid)); PartName = CurrentESP->Volume->PartName; + FsName = CurrentESP->Volume->FsName; VolName = CurrentESP->Volume->VolName; - if (PartName && (StrLen(PartName) > 0) && VolName && (StrLen(VolName) > 0) && - !MyStriCmp(VolName, PartName)) { - Temp = PoolPrint(L"%s - '%s', aka '%s'", GuidStr, PartName, VolName); + if (PartName && (StrLen(PartName) > 0) && FsName && (StrLen(FsName) > 0) && + !MyStriCmp(FsName, PartName)) { + Temp = PoolPrint(L"%s - '%s', aka '%s'", GuidStr, PartName, FsName); + } else if (FsName && (StrLen(FsName) > 0)) { + Temp = PoolPrint(L"%s - '%s'", GuidStr, FsName); + } else if (PartName && (StrLen(PartName) > 0)) { + Temp = PoolPrint(L"%s - '%s'", GuidStr, PartName); } else if (VolName && (StrLen(VolName) > 0)) { - Temp = PoolPrint(L"%s - '%s'", GuidStr, VolName); + Temp = PoolPrint(L"%s - '%s'", GuidStr, VolName); } else { Temp = PoolPrint(L"%s - no name", GuidStr); } + LOG(3, LOG_LINE_NORMAL, L"Adding '%s' to UI list of ESPs"); MyFreePool(&GuidStr); MenuEntryItem->Title = Temp; MenuEntryItem->Tag = TAG_RETURN; @@ -132,6 +143,7 @@ } // if } else { DisplaySimpleMessage(L"Information", L"No eligible ESPs found"); + LOG(2, LOG_LINE_NORMAL, L"No ESPs found"); } // if return ChosenVolume; } // REFIT_VOLUME *PickOneESP() @@ -148,6 +160,7 @@ EFI_FILE_INFO *NewInfo, *Buffer = NULL; UINTN NewInfoSize; + LOG(3, LOG_LINE_NORMAL, L"Trying to rename '%s' to '%s'", OldName, NewName); Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FilePtr, OldName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); if (Status == EFI_SUCCESS) { @@ -187,6 +200,7 @@ EFI_STATUS Status = EFI_SUCCESS; CHAR16 *NewName; + LOG(3, LOG_LINE_NORMAL, L"Backing up '%s'", FileName); if ((BaseDir == NULL) || (FileName == NULL)) return EFI_INVALID_PARAMETER; @@ -260,6 +274,9 @@ MyFreePool(DestFile); MyFreePool(Buffer); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d when copying '%s' to '%s'", Status, SourceName, DestName); + } return (Status); } // EFI_STATUS CopyOneFile() @@ -291,19 +308,22 @@ // even though HFS+ is not technically a Linux filesystem, since HFS+ CAN be used // as a Linux /boot partition. That's weird, but it does work. static EFI_STATUS CopyDrivers(IN EFI_FILE *SourceDirPtr, - IN CHAR16 *SourceDirName, - IN EFI_FILE *DestDirPtr, - IN CHAR16 *DestDirName) { + IN CHAR16 *SourceDirName, + IN EFI_FILE *DestDirPtr, + IN CHAR16 *DestDirName) { CHAR16 *DestFileName = NULL, *SourceFileName = NULL; CHAR16 *DriverName = NULL; // Note: Assign to string constants; do not free. EFI_STATUS Status = EFI_SUCCESS; + EFI_STATUS WorstStatus = EFI_SUCCESS; BOOLEAN DriverCopied[NUM_FS_TYPES]; UINTN i; for (i = 0; i < NUM_FS_TYPES; i++) DriverCopied[i] = FALSE; - for (i = 0; (i < VolumesCount) && (Status == EFI_SUCCESS); i++) { + LOG(3, LOG_LINE_NORMAL, L"Scanning %d volumes for identifiable filesystems", VolumesCount); + for (i = 0; i < VolumesCount; i++) { + LOG(1, LOG_LINE_NORMAL, L"Looking for driver for volume # %d, '%s'", i, Volumes[i]->VolName); DriverName = NULL; switch (Volumes[i]->FSType) { @@ -355,13 +375,16 @@ if (DriverName) { SourceFileName = PoolPrint(L"%s\\%s%s", SourceDirName, DriverName, INST_PLATFORM_EXTENSION); DestFileName = PoolPrint(L"%s\\%s%s", DestDirName, DriverName, INST_PLATFORM_EXTENSION); + LOG(1, LOG_LINE_NORMAL, L"Trying to copy driver for %s", DriverName); Status = CopyOneFile(SourceDirPtr, SourceFileName, DestDirPtr, DestFileName); + if (EFI_ERROR(Status)) + WorstStatus = Status; MyFreePool(SourceFileName); MyFreePool(DestFileName); } // if } // for - return (Status); + return (WorstStatus); } // EFI_STATUS CopyDrivers() // Copy all the files from the source to *TargetDir @@ -369,7 +392,8 @@ REFIT_VOLUME *SourceVolume = NULL; // Do not free CHAR16 *SourceFile = NULL, *SourceDir, *ConfFile; CHAR16 *SourceDriversDir, *TargetDriversDir, *RefindName; - UINTN Status; + EFI_STATUS Status; + EFI_STATUS WorstStatus = EFI_SUCCESS; FindVolumeAndFilename(GlobalConfig.SelfDevicePath, &SourceVolume, &SourceFile); SourceDir = FindPath(SourceFile); @@ -379,13 +403,17 @@ Status = CopyOneFile(SourceVolume->RootDir, SourceFile, TargetDir, RefindName); MyFreePool(SourceFile); MyFreePool(RefindName); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error copying rEFInd binary; installation has failed"); + Status = WorstStatus = EFI_ABORTED; + } - // Now copy the config file -- but: - // - Copy refind.conf-sample, not refind.conf, if it's available, to - // avoid picking up live-disk-specific customizations. - // - Do not overwrite an existing refind.conf at the target; instead, - // copy to refind.conf-sample if refind.conf is present. if (Status == EFI_SUCCESS) { + // Now copy the config file -- but: + // - Copy refind.conf-sample, not refind.conf, if it's available, to + // avoid picking up live-disk-specific customizations. + // - Do not overwrite an existing refind.conf at the target; instead, + // copy to refind.conf-sample if refind.conf is present. ConfFile = PoolPrint(L"%s\\refind.conf-sample", SourceDir); if (FileExists(SourceVolume->RootDir, ConfFile)) { StrCpy(SourceFile, ConfFile); @@ -393,31 +421,38 @@ SourceFile = PoolPrint(L"%s\\refind.conf", SourceDir); } MyFreePool(ConfFile); + // Note: CopyOneFile() logs errors if they occur if (FileExists(TargetDir, L"\\EFI\\refind\\refind.conf")) { Status = CopyOneFile(SourceVolume->RootDir, SourceFile, TargetDir, L"EFI\\refind\\refind.conf-sample"); } else { Status = CopyOneFile(SourceVolume->RootDir, SourceFile, TargetDir, L"EFI\\refind\\refind.conf"); } + if (EFI_ERROR(Status)) + WorstStatus = Status; MyFreePool(SourceFile); - } - // Now copy icons.... - if (Status == EFI_SUCCESS) { + // Now copy icons.... SourceFile = PoolPrint(L"%s\\icons", SourceDir); Status = CopyDirectory(SourceVolume->RootDir, SourceFile, TargetDir, L"EFI\\refind\\icons"); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d copying icons directory", Status); + WorstStatus = Status; + } MyFreePool(SourceFile); - } - // Now copy drivers.... - if (Status == EFI_SUCCESS) { + // Now copy drivers.... SourceDriversDir = PoolPrint(L"%s\\%s", SourceDir, INST_DRIVERS_SUBDIR); TargetDriversDir = PoolPrint(L"EFI\\refind\\%s", INST_DRIVERS_SUBDIR); Status = CopyDrivers(SourceVolume->RootDir, SourceDriversDir, TargetDir, TargetDriversDir); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d copying drivers", Status); + WorstStatus = Status; + } MyFreePool(SourceDriversDir); MyFreePool(TargetDriversDir); } MyFreePool(SourceDir); - return (Status); + return (WorstStatus); } // EFI_STATUS CopyFiles() // Create the BOOT.CSV file used by the fallback.efi/fbx86.efi program. @@ -441,19 +476,40 @@ } // if MyFreePool(Contents); } // if + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d when writing BOOT.CSV file", Status); + } } // VOID CreateFallbackCSV() static BOOLEAN CopyRefindFiles(IN EFI_FILE *TargetDir) { - EFI_STATUS Status = EFI_SUCCESS; + EFI_STATUS Status = EFI_SUCCESS, Status2; - if (FileExists(TargetDir, L"\\EFI\\refind\\icons")) + if (FileExists(TargetDir, L"\\EFI\\refind\\icons")) { Status = BackupOldFile(TargetDir, L"\\EFI\\refind\\icons"); - if (Status == EFI_SUCCESS) + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error when backing up icons"); + } + } + if (Status == EFI_SUCCESS) { Status = CreateDirectories(TargetDir); - if (Status == EFI_SUCCESS) - Status = CopyFiles(TargetDir); - if (Status == EFI_SUCCESS) - CreateFallbackCSV(TargetDir); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error when creating target directory"); + } + } + if (Status == EFI_SUCCESS) { + // Check status and log if it's an error; but do not pass on the + // result code unless it's EFI_ABORTED, since it may not be a + // critical error. + Status2 = CopyFiles(TargetDir); + if (EFI_ERROR(Status2)) { + if (Status2 == EFI_ABORTED) { + Status = EFI_ABORTED; + } else { + DisplaySimpleMessage(L"Warning", L"Error copying some files"); + } + } + } + CreateFallbackCSV(TargetDir); return Status; } // BOOLEAN CopyRefindFiles() @@ -632,6 +688,7 @@ REFIT_VOLUME *SelectedESP; // Do not free UINTN Status; + LOG(1, LOG_LINE_NORMAL, L"Installing rEFInd to an ESP"); AllESPs = FindAllESPs(); SelectedESP = PickOneESP(AllESPs); if (SelectedESP) { @@ -653,15 +710,8 @@ * ***********************/ -// A linked-list data structure intended to hold a list of all the EFI boot -// entries on the computer.... -typedef struct _boot_entry_list { - EFI_BOOT_ENTRY BootEntry; - struct _boot_entry_list *NextBootEntry; -} BOOT_ENTRY_LIST; - // Create a list of Boot entries matching the BootOrder list. -static BOOT_ENTRY_LIST * FindBootOrderEntries(VOID) { +BOOT_ENTRY_LIST * FindBootOrderEntries(VOID) { UINTN Status = EFI_SUCCESS, i; UINT16 *BootOrder = NULL; UINTN VarSize, ListSize; @@ -669,6 +719,7 @@ CHAR16 *Contents = NULL; BOOT_ENTRY_LIST *L, *ListStart = NULL, *ListEnd = NULL; // return value; do not free + LOG(1, LOG_LINE_NORMAL, L"Finding boot order entries"); Status = EfivarGetRaw(&GlobalGuid, L"BootOrder", (CHAR8**) &BootOrder, &VarSize); if (Status != EFI_SUCCESS) return NULL; @@ -708,7 +759,7 @@ } // BOOT_ENTRY_LIST * FindBootOrderEntries() // Delete a linked-list BOOT_ENTRY_LIST data structure -static VOID DeleteBootOrderEntries(BOOT_ENTRY_LIST *Entries) { +VOID DeleteBootOrderEntries(BOOT_ENTRY_LIST *Entries) { BOOT_ENTRY_LIST *Current; while (Entries != NULL) { @@ -796,6 +847,7 @@ CHAR8 *Contents; CHAR16 *VarName; + LOG(1, LOG_LINE_NORMAL, L"Deleting invalid boot entries from internal BootOrder list"); Status = EfivarGetRaw(&GlobalGuid, L"BootOrder", (CHAR8**) &BootOrder, &VarSize); if (Status == EFI_SUCCESS) { ListSize = VarSize / sizeof(UINT16); @@ -823,6 +875,7 @@ UINTN BootNum = 0, Operation; CHAR16 *Name, *Message; + LOG(1, LOG_LINE_NORMAL, L"Managing boot order list"); Entries = FindBootOrderEntries(); Operation = PickOneBootOption(Entries, &BootNum); if (Operation == EFI_BOOT_OPTION_DELETE) { diff -Nru refind-0.12.0/refind/install.h refind-0.13.2/refind/install.h --- refind-0.12.0/refind/install.h 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/refind/install.h 2021-02-21 21:40:58.000000000 +0000 @@ -52,7 +52,16 @@ // CHAR16 *Arguments; // Part of original data structure, but we don't use } EFI_BOOT_ENTRY; +// A linked-list data structure intended to hold a list of all the EFI boot +// entries on the computer.... +typedef struct _boot_entry_list { + EFI_BOOT_ENTRY BootEntry; + struct _boot_entry_list *NextBootEntry; +} BOOT_ENTRY_LIST; + VOID InstallRefind(VOID); +BOOT_ENTRY_LIST * FindBootOrderEntries(VOID); +VOID DeleteBootOrderEntries(BOOT_ENTRY_LIST *Entries); VOID ManageBootorder(VOID); #endif diff -Nru refind-0.12.0/refind/launch_efi.c refind-0.13.2/refind/launch_efi.c --- refind-0.12.0/refind/launch_efi.c 2020-02-29 14:01:08.000000000 +0000 +++ refind-0.13.2/refind/launch_efi.c 2021-03-08 03:50:07.000000000 +0000 @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), or (at your option) any later version. @@ -63,6 +63,7 @@ #include "driver_support.h" #include "../include/refit_call_wrapper.h" #include "launch_efi.h" +#include "log.h" #include "scan.h" // @@ -137,6 +138,8 @@ Header[Size+2] == 0 && Header[Size+3] == 0 && *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) || (*(UINT32 *)&Header == FAT_ARCH)); + LOG(1, LOG_LINE_NORMAL, L"'%s' %s a valid loader", FileName, + IsValid ? L"is" : L"is NOT"); #endif return IsValid; } // BOOLEAN IsValidLoader() @@ -154,8 +157,10 @@ EFI_HANDLE ChildImageHandle, ChildImageHandle2; EFI_DEVICE_PATH *DevicePath; EFI_LOADED_IMAGE *ChildLoadedImage = NULL; - CHAR16 ErrorInfo[256]; + CHAR16 *ErrorInfo; CHAR16 *FullLoadOptions = NULL; + CHAR16 *EspGUID; + EFI_GUID SystemdGuid = SYSTEMD_GUID_VALUE; // set load options if (LoadOptions != NULL) { @@ -166,6 +171,10 @@ // when passing options to Apple's boot.efi... } // if } // if (LoadOptions != NULL) + LOG(1, LOG_LINE_NORMAL, L"Starting %s", ImageTitle); + LOG(1, LOG_LINE_NORMAL, L"Using load options '%s'", FullLoadOptions ? FullLoadOptions : L""); + if (IsDriver) + LOG(1, LOG_LINE_NORMAL, L"Note: %s is a driver", ImageTitle); if (Verbose) Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions ? FullLoadOptions : L""); @@ -198,19 +207,23 @@ // NOTE: This doesn't check the return status or handle errors. It could // conceivably do weird things if, say, rEFInd were on a USB drive that the // user pulls before launching a program. + LOG(3, LOG_LINE_NORMAL, L"Employing Shim LoadImage() hack"); refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, GlobalConfig.SelfDevicePath, NULL, 0, &ChildImageHandle2); } } else { Print(L"Invalid loader file!\n"); + LOG(1, LOG_LINE_NORMAL, L"Invalid loader file!"); ReturnStatus = EFI_LOAD_ERROR; } if ((Status == EFI_ACCESS_DENIED) || (Status == EFI_SECURITY_VIOLATION)) { + LOG(1, LOG_LINE_NORMAL, L"Secure boot error while loading '%s'; Status = %d", ImageTitle, Status); WarnSecureBootError(ImageTitle, Verbose); goto bailout; } - SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle); + ErrorInfo = PoolPrint(L"while loading %s", ImageTitle); if (CheckError(Status, ErrorInfo)) { + MyFreePool(ErrorInfo); goto bailout; } @@ -224,13 +237,30 @@ // turn control over to the image // TODO: (optionally) re-enable the EFI watchdog timer! + if ((GlobalConfig.WriteSystemdVars) && ((OSType == 'L') || (OSType == 'E') || (OSType == 'G'))) { + // Tell systemd what ESP rEFInd used + EspGUID = GuidAsString(&(SelfVolume->PartGuid)); + LOG(1, LOG_LINE_NORMAL, L"Setting systemd's LoaderDevicePartUUID variable to %s", EspGUID); + Status = EfivarSetRaw(&SystemdGuid, L"LoaderDevicePartUUID", (CHAR8 *) EspGUID, + StrLen(EspGUID) * 2 + 2, TRUE); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, + L"Error %d when trying to set LoaderDevicePartUUID EFI variable", Status); + } + MyFreePool(EspGUID); + } // if write systemd EFI variables + // close open file handles + LOG(1, LOG_LINE_NORMAL, L"Launching '%s'", ImageTitle); UninitRefitLib(); + + // Actually launch the program.... 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); + ErrorInfo = PoolPrint(L"returned from %s", ImageTitle); CheckError(Status, ErrorInfo); + MyFreePool(ErrorInfo); if (IsDriver) { // Below should have no effect on most systems, but works // around bug with some EFIs that prevents filesystem drivers @@ -240,6 +270,7 @@ // re-open file handles ReinitRefitLib(); + LOG(1, LOG_LINE_NORMAL, L"Program has returned %d", Status); bailout_unload: // unload the image, we don't care if it works or not... @@ -251,6 +282,7 @@ if (!IsDriver) FinishExternalScreen(); + LOG(1, LOG_LINE_THIN_SEP, L"Next loader"); return ReturnStatus; } /* EFI_STATUS StartEFIImage() */ @@ -272,12 +304,38 @@ if (err != EFI_SUCCESS) return err; + LOG(1, LOG_LINE_SEPARATOR, L"Rebooting into the computer's firmware"); + UninitRefitLib(); refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); - Print(L"Error calling ResetSystem: %r", err); + ReinitRefitLib(); + LOG(1, LOG_LINE_NORMAL, L"Error calling ResetSystem: %r", err); + Print(L"Error calling ResetSystem: %r\n", err); PauseForKey(); return err; } // EFI_STATUS RebootIntoFirmware() +// Reboot into a loader defined in the EFI's NVRAM +VOID RebootIntoLoader(LOADER_ENTRY *Entry) { + EFI_STATUS Status; + + LOG(1, LOG_LINE_SEPARATOR, L"Rebooting into EFI loader '%s' (Boot%04x)", + Entry->Title, Entry->EfiBootNum); + Status = EfivarSetRaw(&GlobalGuid, L"BootNext", (CHAR8*) &(Entry->EfiBootNum), sizeof(UINT16), TRUE); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error: %d", Status); + Print(L"Error: %d\n", Status); + return; + } + StoreLoaderName(Entry->me.Title); + LOG(1, LOG_LINE_NORMAL, L"Attempting to reboot....", Entry->Title, Entry->EfiBootNum); + UninitRefitLib(); + refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); + ReinitRefitLib(); + LOG(1, LOG_LINE_NORMAL, L"Error calling ResetSystem: %r", Status); + Print(L"Error calling ResetSystem: %r\n", Status); + PauseForKey(); +} // RebootIntoLoader() + // // EFI OS loader functions // @@ -289,6 +347,7 @@ UINT32 msr = 0x3a; UINT32 low_bits = 0, high_bits = 0; + LOG(1, LOG_LINE_NORMAL, L"Attempting to enable and lock VMX"); // is VMX active ? __asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr)); @@ -302,20 +361,32 @@ #endif } // VOID DoEnableAndLockVMX() +// Directly launch an EFI boot loader (or similar program) VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName) { + CHAR16 *LoaderPath; + + LOG(1, LOG_LINE_SEPARATOR, L"Launching '%s'", SelectionName); if (GlobalConfig.EnableAndLockVMX) { DoEnableAndLockVMX(); } + LoaderPath = Basename(Entry->LoaderPath); BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS"); StoreLoaderName(SelectionName); StartEFIImage(Entry->Volume, Entry->LoaderPath, Entry->LoadOptions, - Basename(Entry->LoaderPath), Entry->OSType, !Entry->UseGraphicsMode, FALSE); + LoaderPath, Entry->OSType, !Entry->UseGraphicsMode, FALSE); + MyFreePool(LoaderPath); } // VOID StartLoader() +// Launch an EFI tool (a shell, SB management utility, etc.) VOID StartTool(IN LOADER_ENTRY *Entry) { + CHAR16 *LoaderPath; + + LOG(1, LOG_LINE_SEPARATOR, L"Starting '%s'", Entry->me.Title); BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start " as assigned below StoreLoaderName(Entry->me.Title); + LoaderPath = Basename(Entry->LoaderPath); StartEFIImage(Entry->Volume, Entry->LoaderPath, Entry->LoadOptions, - Basename(Entry->LoaderPath), Entry->OSType, TRUE, FALSE); + LoaderPath, Entry->OSType, TRUE, FALSE); + MyFreePool(LoaderPath); } /* VOID StartTool() */ diff -Nru refind-0.12.0/refind/launch_efi.h refind-0.13.2/refind/launch_efi.h --- refind-0.12.0/refind/launch_efi.h 2020-02-20 14:37:44.000000000 +0000 +++ refind-0.13.2/refind/launch_efi.h 2021-02-21 21:40:58.000000000 +0000 @@ -81,6 +81,7 @@ EFI_STATUS RebootIntoFirmware(VOID); VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName); VOID StartTool(IN LOADER_ENTRY *Entry); +VOID RebootIntoLoader(LOADER_ENTRY *Entry); #endif diff -Nru refind-0.12.0/refind/launch_legacy.c refind-0.13.2/refind/launch_legacy.c --- refind-0.12.0/refind/launch_legacy.c 2020-02-20 14:37:44.000000000 +0000 +++ refind-0.13.2/refind/launch_legacy.c 2021-03-13 18:39:11.000000000 +0000 @@ -64,6 +64,7 @@ #include "screen.h" #include "../include/syslinux_mbr.h" #include "mystrings.h" +#include "log.h" #include "../EfiLib/BdsHelper.h" #include "../EfiLib/legacy.h" #include "../include/Handle.h" @@ -179,15 +180,15 @@ static EFI_STATUS WriteBootDiskHint(IN EFI_DEVICE_PATH *WholeDiskDevicePath) { - EFI_STATUS Status; + EFI_STATUS Status; - Status = refit_call5_wrapper(RT->SetVariable, L"BootCampHD", &AppleVariableVendorID, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, - GetDevicePathSize(WholeDiskDevicePath), WholeDiskDevicePath); - if (EFI_ERROR(Status)) - return Status; + Status = EfivarSetRaw(&AppleVariableVendorID, L"BootCampHD", + (CHAR8*) WholeDiskDevicePath, + GetDevicePathSize(WholeDiskDevicePath), TRUE); + if (EFI_ERROR(Status)) + return Status; - return EFI_SUCCESS; + return EFI_SUCCESS; } // @@ -332,7 +333,6 @@ EFI_HANDLE ChildImageHandle; EFI_LOADED_IMAGE *ChildLoadedImage = NULL; UINTN DevicePathIndex; - CHAR16 ErrorInfo[256]; CHAR16 *FullLoadOptions = NULL; if (ErrorInStep != NULL) @@ -353,8 +353,7 @@ break; } } // for - SPrint(ErrorInfo, 255, L"while loading legacy loader"); - if (CheckError(Status, ErrorInfo)) { + if (CheckError(Status, L"while loading legacy loader")) { if (ErrorInStep != NULL) *ErrorInStep = 1; goto bailout; @@ -373,12 +372,13 @@ // TODO: (optionally) re-enable the EFI watchdog timer! // close open file handles + LOG(1, LOG_LINE_NORMAL, L"Launching Mac-style BIOS/CSM/legacy loader"); UninitRefitLib(); ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL); // control returns here when the child image calls Exit() - SPrint(ErrorInfo, 255, L"returned from legacy loader"); - if (CheckError(Status, ErrorInfo)) { + if (CheckError(Status, L"returned from legacy loader")) { + LOG(1, LOG_LINE_NORMAL, L"returned from legacy loader"); if (ErrorInStep != NULL) *ErrorInStep = 3; } @@ -402,6 +402,7 @@ UINTN ErrorInStep = 0; EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS]; + LOG(1, LOG_LINE_NORMAL, L"Starting Mac-style BIOS/CSM/legacy loader '%s'", SelectionName); BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)"); BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE); @@ -435,13 +436,17 @@ // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL VOID StartLegacyUEFI(LEGACY_ENTRY *Entry, CHAR16 *SelectionName) { + LOG(1, LOG_LINE_SEPARATOR, L"Launching UEFI-style BIOS/CSM/legacy OS '%s'", SelectionName); BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)"); StoreLoaderName(SelectionName); + UninitRefitLib(); BdsLibConnectDevicePath (Entry->BdsOption->DevicePath); BdsLibDoLegacyBoot(Entry->BdsOption); // If we get here, it means that there was a failure.... + ReinitRefitLib(); + LOG(1, LOG_LINE_NORMAL, L"Failure booting legacy (BIOS) OS."); Print(L"Failure booting legacy (BIOS) OS."); PauseForKey(); FinishExternalScreen(); @@ -467,46 +472,43 @@ else VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD"; - LegacyTitle = AllocateZeroPool(256 * sizeof(CHAR16)); - if (LegacyTitle != NULL) - SPrint(LegacyTitle, 255, L"Boot %s from %s", LoaderTitle, VolDesc); + LegacyTitle = PoolPrint(L"Boot %s from %s", LoaderTitle, VolDesc); + LOG(1, LOG_LINE_NORMAL, L"Adding BIOS/CSM/legacy entry for '%s'", LegacyTitle); if (IsInSubstring(LegacyTitle, GlobalConfig.DontScanVolumes)) { - MyFreePool(LegacyTitle); - return NULL; + MyFreePool(LegacyTitle); + return NULL; } // if // prepare the menu entry Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); - Entry->me.Title = LegacyTitle; - Entry->me.Tag = TAG_LEGACY; - Entry->me.Row = 0; + Entry->me.Title = LegacyTitle; + Entry->me.Tag = TAG_LEGACY; + Entry->me.Row = 0; Entry->me.ShortcutLetter = ShortcutLetter; - Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE); - Entry->me.BadgeImage = Volume->VolBadgeImage; - Entry->Volume = Volume; - Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : - ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD"); - Entry->Enabled = TRUE; + Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE); + Entry->me.BadgeImage = Volume->VolBadgeImage; + Entry->Volume = Volume; + Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : + ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD"); + Entry->Enabled = TRUE; // create the submenu - SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); - SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc); + SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); + SubScreen->Title = PoolPrint(L"Boot Options for %s on %s", LoaderTitle, VolDesc); SubScreen->TitleImage = Entry->me.Image; - SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1); + SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1); if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { - SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); } else { - SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2); + 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", LoaderTitle); - SubEntry->me.Tag = TAG_LEGACY; - SubEntry->Volume = Entry->Volume; - SubEntry->LoadOptions = Entry->LoadOptions; + SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); + SubEntry->me.Title = PoolPrint(L"Boot %s", LoaderTitle); + SubEntry->me.Tag = TAG_LEGACY; + SubEntry->Volume = Entry->Volume; + SubEntry->LoadOptions = Entry->LoadOptions; AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); AddMenuEntry(SubScreen, &MenuEntryReturn); @@ -527,7 +529,7 @@ CHAR16 *LegacyDescription = StrDuplicate(BdsOption->Description); if (IsInSubstring(LegacyDescription, GlobalConfig.DontScanVolumes)) - return NULL; + return NULL; // Remove stray spaces, since many EFIs produce descriptions with lots of // extra spaces, especially at the end; this throws off centering of the @@ -536,34 +538,32 @@ // prepare the menu entry Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY)); - Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16)); - SPrint(Entry->me.Title, 255, L"Boot legacy OS from %s", LegacyDescription); - Entry->me.Tag = TAG_LEGACY_UEFI; - Entry->me.Row = 0; + Entry->me.Title = PoolPrint(L"Boot legacy OS from %s", LegacyDescription); + LOG(1, LOG_LINE_NORMAL, L"Adding UEFI-style BIOS/CSM/legacy entry for '%s'", Entry->me.Title); + 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->BdsOption = BdsOption; - Entry->Enabled = TRUE; + 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->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->Title = StrDuplicate(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); + SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR); } else { - SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2); + 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 = AllocateZeroPool(sizeof(LEGACY_ENTRY)); + SubEntry->me.Title = PoolPrint(L"Boot %s", LegacyDescription); SubEntry->me.Tag = TAG_LEGACY_UEFI; Entry->BdsOption = BdsOption; AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry); @@ -594,21 +594,23 @@ BBS_BBS_DEVICE_PATH *BbsDevicePath = NULL; BOOLEAN SearchingForUsb = FALSE; + LOG(1, LOG_LINE_NORMAL, L"Scanning for a UEFI-style BIOS/CSM/legacy OS"); 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 = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); + Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, + NULL, (VOID **) &LegacyBios); if (EFI_ERROR (Status)) - return; + return; // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them, // so we set DiskType inappropriately elsewhere in the program and // "translate" it here. if (DiskType == BBS_USB) { - DiskType = BBS_HARDDISK; - SearchingForUsb = TRUE; + DiskType = BBS_HARDDISK; + SearchingForUsb = TRUE; } // if // Grab the boot order @@ -622,82 +624,80 @@ { // 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]); + SPrint(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)) { - // USB flash drives appear as hard disks with certain media flags set. - // Look for this, and if present, pass it on with the (technically - // incorrect, but internally useful) BBS_TYPE_USB flag set. - if (DiskType == BBS_HARDDISK) { - if (SearchingForUsb && (BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { - AddLegacyEntryUEFI(BdsOption, BBS_USB); - } else if (!SearchingForUsb && !(BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { + 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)) { + // USB flash drives appear as hard disks with certain media flags set. + // Look for this, and if present, pass it on with the (technically + // incorrect, but internally useful) BBS_TYPE_USB flag set. + if (DiskType == BBS_HARDDISK) { + if (SearchingForUsb && (BbsDevicePath->StatusFlag & + (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { + AddLegacyEntryUEFI(BdsOption, BBS_USB); + } else if (!SearchingForUsb && !(BbsDevicePath->StatusFlag & + (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) { + AddLegacyEntryUEFI(BdsOption, DiskType); + } + } else { AddLegacyEntryUEFI(BdsOption, DiskType); - } - } else { - AddLegacyEntryUEFI(BdsOption, DiskType); - } // if/else - } // if + } // if/else + } // if } // if (BdsOption != NULL) Index++; } // while } /* static VOID ScanLegacyUEFI() */ static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) { - UINTN VolumeIndex2; - BOOLEAN ShowVolume, HideIfOthersFound; + UINTN VolumeIndex2; + BOOLEAN ShowVolume, HideIfOthersFound; - ShowVolume = FALSE; - HideIfOthersFound = FALSE; -// if (Volume->IsAppleLegacy) { -// Print(L" Volume is Apple legacy\n"); -// ShowVolume = TRUE; -// HideIfOthersFound = TRUE; -// } else - if (Volume->HasBootCode) { - ShowVolume = TRUE; - if (Volume->BlockIO == Volume->WholeDiskBlockIO && - Volume->BlockIOOffset == 0 && - Volume->OSName == NULL) - // this is a whole disk (MBR) entry; hide if we have entries for partitions - HideIfOthersFound = TRUE; - } - if (HideIfOthersFound) { - // check for other bootable entries on the same disk - for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) { - if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode && - Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO) - ShowVolume = FALSE; - } - } + ShowVolume = FALSE; + HideIfOthersFound = FALSE; + if (Volume->HasBootCode) { + ShowVolume = TRUE; + if (Volume->BlockIO == Volume->WholeDiskBlockIO && + Volume->BlockIOOffset == 0 && + Volume->OSName == NULL) + // this is a whole disk (MBR) entry; hide if we have entries for partitions + HideIfOthersFound = TRUE; + } + if (HideIfOthersFound) { + // check for other bootable entries on the same disk + for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) { + if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode && + Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO) + ShowVolume = FALSE; + } + } - if (ShowVolume) - AddLegacyEntry(NULL, Volume); + if (ShowVolume) + AddLegacyEntry(NULL, Volume); } // static VOID ScanLegacyVolume() // Scan attached optical discs for legacy (BIOS) boot code // and add anything found to the list.... VOID ScanLegacyDisc(VOID) { - UINTN VolumeIndex; - REFIT_VOLUME *Volume; + UINTN VolumeIndex; + REFIT_VOLUME *Volume; - 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); - } + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for BIOS/CSM/legacy-mode optical discs"); + 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); + } } /* VOID ScanLegacyDisc() */ // Scan internal hard disks for legacy (BIOS) boot code @@ -707,6 +707,7 @@ UINTN VolumeIndex; REFIT_VOLUME *Volume; + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for BIOS/CSM/legacy-mode internal disks"); if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) { for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { Volume = Volumes[VolumeIndex]; @@ -724,63 +725,67 @@ // and add anything found to the list.... VOID ScanLegacyExternal(VOID) { - UINTN VolumeIndex; - REFIT_VOLUME *Volume; + UINTN VolumeIndex; + REFIT_VOLUME *Volume; - 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) { - // TODO: This actually doesn't do anything useful; leaving in hopes of - // fixing it later.... - ScanLegacyUEFI(BBS_USB); - } + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for BIOS/CSM/legacy-mode external disks"); + 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) { + // TODO: This actually doesn't do anything useful; leaving in hopes of + // fixing it later.... + ScanLegacyUEFI(BBS_USB); + } } /* VOID ScanLegacyExternal() */ // Determine what (if any) type of legacy (BIOS) boot support is available VOID FindLegacyBootType(VOID) { - EFI_STATUS Status; - EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; - GlobalConfig.LegacyType = LEGACY_TYPE_NONE; + GlobalConfig.LegacyType = LEGACY_TYPE_NONE; - // UEFI-style legacy BIOS support is available only with some EFI implementations.... - Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios); - if (!EFI_ERROR (Status)) - GlobalConfig.LegacyType = LEGACY_TYPE_UEFI; - - // 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; + // UEFI-style legacy BIOS support is available only with some EFI implementations.... + Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, + NULL, (VOID **) &LegacyBios); + if (!EFI_ERROR (Status)) + GlobalConfig.LegacyType = LEGACY_TYPE_UEFI; + + // 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; } // VOID FindLegacyBootType // Warn the user if legacy OS scans are enabled but the firmware can't support them.... VOID WarnIfLegacyProblems(VOID) { - BOOLEAN found = FALSE; - UINTN i = 0; + 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' || - 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 your computer lacks\n"); - Print(L"the necessary Compatibility Support Module (CSM) support or that support is\n"); - Print(L"disabled in your firmware.\n"); - PauseForKey(); - } // if (found) - } // if no legacy support + if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) { + do { + if (GlobalConfig.ScanFor[i] == 'H' || GlobalConfig.ScanFor[i] == 'h' || + GlobalConfig.ScanFor[i] == 'C' || GlobalConfig.ScanFor[i] == 'c' || + GlobalConfig.ScanFor[i] == 'B' || GlobalConfig.ScanFor[i] == 'b') + found = TRUE; + i++; + } while ((i < NUM_SCAN_OPTIONS) && (!found)); + + if (found) { + LOG(1, LOG_LINE_NORMAL, L"BIOS/CSM/legacy support enabled in rEFInd but unavailable in EFI!"); + 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 your computer lacks\n"); + Print(L"the necessary Compatibility Support Module (CSM) support or that support is\n"); + Print(L"disabled in your firmware.\n"); + PauseForKey(); + } // if (found) + } // if no legacy support } // VOID WarnIfLegacyProblems() diff -Nru refind-0.12.0/refind/lib.c refind-0.13.2/refind/lib.c --- refind-0.12.0/refind/lib.c 2020-03-07 13:51:43.000000000 +0000 +++ refind-0.13.2/refind/lib.c 2021-03-10 13:52:19.000000000 +0000 @@ -63,6 +63,8 @@ #include "../include/RemovableMedia.h" #include "gpt.h" #include "config.h" +#include "driver_support.h" +#include "log.h" #include "mystrings.h" #ifdef __MAKEWITH_GNUEFI @@ -88,6 +90,7 @@ #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define BTRFS_SIGNATURE "_BHRfS_M" #define XFS_SIGNATURE "XFSB" +#define JFS_SIGNATURE "JFS1" #define NTFS_SIGNATURE "NTFS " #define FAT12_SIGNATURE "FAT12 " #define FAT16_SIGNATURE "FAT16 " @@ -115,7 +118,9 @@ REFIT_VOLUME *SelfVolume = NULL; REFIT_VOLUME **Volumes = NULL; UINTN VolumesCount = 0; -extern EFI_GUID RefindGuid; +extern EFI_GUID RefindGuid; + +EFI_FILE *gVarsDir = NULL; // Maximum size for disk sectors #define SECTOR_SIZE 4096 @@ -281,14 +286,14 @@ RemainingDevicePath = Volume->DevicePath; Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &DeviceHandle); - if (!EFI_ERROR(Status)) { + if (EFI_ERROR(Status)) { + CheckError(Status, L"from LocateDevicePath"); + } else { Volume->DeviceHandle = DeviceHandle; // get the root directory Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - - } else - CheckError(Status, L"from LocateDevicePath"); + } } if (Volume->WholeDiskDevicePath != NULL) { @@ -313,6 +318,12 @@ // called before running external programs to close open file handles VOID UninitRefitLib(VOID) { + // Closing the log file is unnecessary on most systems; however, at + // least one I own (with an ASRock FM2A88M Extreme 4+ motherboard) + // produces 0-length log files if the file is not closed prior to + // launching a follow-on program. Thus, take care of this here.... + StopLogging(); + // This piece of code was made to correspond to weirdness in ReinitRefitLib(). // See the comment on it there. if(SelfRootDir == SelfVolume->RootDir) @@ -334,6 +345,8 @@ // called after running external programs to re-open file handles EFI_STATUS ReinitRefitLib(VOID) { + EFI_STATUS Status; + ReinitVolumes(); if ((ST->Hdr.Revision >> 16) == 1) { @@ -354,31 +367,81 @@ SelfRootDir = SelfVolume->RootDir; } // if - return FinishInitRefitLib(); + Status = FinishInitRefitLib(); + if (EFI_ERROR(Status)) { + // if Status shows an error, we may not be able to re-open + // the log file, so disable logging to prevent a subsequent + // hang or other problem.... + GlobalConfig.LogLevel = 0; + } else { + StartLogging(TRUE); + } + return Status; } // // EFI variable read and write functions // +// Create a directory for holding the rEFInd variables, if they're stored on +// disk. This will be in the rEFInd install directory if possible, or on the +// first ESP that rEFInd can identify if not (typically, this means rEFInd is +// on a read-only filesystem, such as HFS+ on a Mac). If neither location can +// be used, sets GlobalConfig.UseNvram to TRUE. Sets the pointer to the +// directory in the file-global gVarsDir variable and returns the success of +// the operation. +EFI_STATUS CreateVarsDir(VOID) { + EFI_STATUS Status = EFI_SUCCESS; + EFI_FILE_HANDLE EspRootDir; + + if (gVarsDir == NULL) { + LOG(1, LOG_LINE_NORMAL, L"Trying to create a 'vars' directory in which to hold variables"); + Status = refit_call5_wrapper(SelfDir->Open, SelfDir, &gVarsDir, L"vars", + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY); + if (EFI_ERROR(Status)) { + Status = egFindESP(&EspRootDir); + if (Status == EFI_SUCCESS) { + LOG(1, LOG_LINE_NORMAL, + L"Trying to create a 'refind-vars' directory on the ESP in which to hold variables"); + Status = refit_call5_wrapper(EspRootDir->Open, EspRootDir, &gVarsDir, L"refind-vars", + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY); + } + } + } + if (EFI_ERROR(Status)) { + GlobalConfig.UseNvram = TRUE; + LOG(1, LOG_LINE_NORMAL, L"Unable to create a directory in which to hold variables; error %d", Status); + LOG(1, LOG_LINE_NORMAL, L"Falling back to NVRAM-based storage"); + } + return Status; +} // EFI_STATUS FindVarsDir() + // Retrieve a raw EFI variable, either from NVRAM or from a disk file under // rEFInd's "vars" subdirectory, depending on GlobalConfig.UseNvram. // Returns EFI status -EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { +EFI_STATUS EfivarGetRaw(IN EFI_GUID *vendor, IN CHAR16 *name, OUT CHAR8 **buffer, OUT UINTN *size OPTIONAL) { UINT8 *buf = NULL; UINTN l; EFI_STATUS Status; - EFI_FILE *VarsDir = NULL; BOOLEAN ReadFromNvram = TRUE; if ((GlobalConfig.UseNvram == FALSE) && GuidsAreEqual(vendor, &RefindGuid)) { - Status = refit_call5_wrapper(SelfDir->Open, SelfDir, &VarsDir, L"vars", - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, EFI_FILE_DIRECTORY); - if (Status == EFI_SUCCESS) - Status = egLoadFile(VarsDir, name, &buf, size); ReadFromNvram = FALSE; - MyFreePool(VarsDir); - } else { + } + LOG(3, LOG_LINE_NORMAL, L"Getting EFI variable '%s' from %s", name, + ReadFromNvram ? L"NVRAM" : L"disk"); + + if (!ReadFromNvram) { + Status = CreateVarsDir(); + if (Status == EFI_SUCCESS) { + Status = egLoadFile(gVarsDir, name, &buf, size); + } else { + ReadFromNvram = TRUE; + } + } + if (ReadFromNvram) { l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; buf = AllocatePool(l); if (!buf) { @@ -387,39 +450,69 @@ } Status = refit_call5_wrapper(RT->GetVariable, name, vendor, NULL, &l, buf); } + if (EFI_ERROR(Status) == EFI_SUCCESS) { *buffer = (CHAR8*) buf; if ((size) && ReadFromNvram) *size = l; } else { + // Note: "Errors" here are common because of attempts to read + // non-existent variables, like Hidden* variables if they haven't + // been set. Hence, log at level 3, not 1. + LOG(3, LOG_LINE_NORMAL, L"Error retrieving EFI variable '%s'", name); MyFreePool(buf); *buffer = NULL; + *size = 0; } return Status; } // EFI_STATUS EfivarGetRaw() -// Set an EFI variable, either to NVRAM or to a disk file under rEFInd's -// "vars" subdirectory, depending on GlobalConfig.UseNvram. +// Set an EFI variable. This is normally done to NVRAM; however, rEFInd-specific +// variables (as determined by the *vendor code) will be saved to a disk file IF +// GlobalConfig.UseNvram == FALSE. +// To minimize wear & tear on NVRAM, this function first reads the contents of +// the variable (if possible), and writes the variable only if its contents +// have changed (or if the variable is new). // Returns EFI status EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { - UINT32 flags; - EFI_FILE *VarsDir = NULL; - EFI_STATUS Status; + UINT32 flags; + EFI_STATUS Status = EFI_SUCCESS, OldStatus; + CHAR8 *OldBuf; + UINTN OldSize; + BOOLEAN WriteToNvram = TRUE; + + if ((GlobalConfig.UseNvram == FALSE) && GuidsAreEqual(vendor, &RefindGuid)) + WriteToNvram = FALSE; + + OldStatus = EfivarGetRaw(vendor, name, &OldBuf, &OldSize); + LOG(2, LOG_LINE_NORMAL, L"Saving EFI variable '%s' to %s", name, + WriteToNvram ? L"NVRAM" : L"disk"); + if ((EFI_ERROR(OldStatus)) || (size != OldSize) || (CompareMem(buf, OldBuf, size) != 0)) { + if (!WriteToNvram) { + Status = CreateVarsDir(); + if (Status == EFI_SUCCESS) { + Status = egSaveFile(gVarsDir, name, (UINT8 *) buf, size); + } else { + WriteToNvram = TRUE; + } + } + if (WriteToNvram) { + flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + if (persistent) + flags |= EFI_VARIABLE_NON_VOLATILE; - if ((GlobalConfig.UseNvram == FALSE) && GuidsAreEqual(vendor, &RefindGuid)) { - Status = refit_call5_wrapper(SelfDir->Open, SelfDir, &VarsDir, L"vars", - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY); - if (Status == EFI_SUCCESS) { - Status = egSaveFile(VarsDir, name, (UINT8 *) buf, size); + Status = refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf); } - MyFreePool(VarsDir); } else { - flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; - if (persistent) - flags |= EFI_VARIABLE_NON_VOLATILE; - - Status = refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf); + LOG(2, LOG_LINE_NORMAL, L"Not writing variable '%s'; it's unchanged", name); } + if (OldStatus == EFI_SUCCESS) { + LOG(3, LOG_LINE_NORMAL, L"Freeing OldBuf"); + MyFreePool(&OldBuf); + } + if (EFI_ERROR(Status)) + LOG(1, LOG_LINE_NORMAL, L"Error %d when writing EFI variable '%s'", Status, name); + return Status; } // EFI_STATUS EfivarSetRaw() @@ -463,49 +556,73 @@ // filesystem type is unknown, a blank (but non-null) string is returned. // The returned variable is a constant that should NOT be freed. static CHAR16 *FSTypeName(IN UINT32 TypeCode) { - CHAR16 *retval = NULL; + CHAR16 *retval = NULL; - switch (TypeCode) { - case FS_TYPE_WHOLEDISK: - retval = L" whole disk"; - break; - case FS_TYPE_FAT: - retval = L" FAT"; - break; - case FS_TYPE_HFSPLUS: - retval = L" HFS+"; - break; - case FS_TYPE_EXT2: - retval = L" ext2"; - break; - case FS_TYPE_EXT3: - retval = L" ext3"; - break; - case FS_TYPE_EXT4: - retval = L" ext4"; - break; - case FS_TYPE_REISERFS: - retval = L" ReiserFS"; - break; - case FS_TYPE_BTRFS: - retval = L" Btrfs"; - break; - case FS_TYPE_XFS: - retval = L" XFS"; - break; - case FS_TYPE_ISO9660: - retval = L" ISO-9660"; - break; - case FS_TYPE_NTFS: - retval = L" NTFS"; - break; - default: - retval = L""; - break; - } // switch - return retval; + switch (TypeCode) { + case FS_TYPE_WHOLEDISK: + retval = L" whole disk"; + break; + case FS_TYPE_FAT: + retval = L" FAT"; + break; + case FS_TYPE_HFSPLUS: + retval = L" HFS+"; + break; + case FS_TYPE_EXT2: + retval = L" ext2"; + break; + case FS_TYPE_EXT3: + retval = L" ext3"; + break; + case FS_TYPE_EXT4: + retval = L" ext4"; + break; + case FS_TYPE_REISERFS: + retval = L" ReiserFS"; + break; + case FS_TYPE_BTRFS: + retval = L" Btrfs"; + break; + case FS_TYPE_XFS: + retval = L" XFS"; + break; + case FS_TYPE_JFS: + retval = L" JFS"; + break; + case FS_TYPE_ISO9660: + retval = L" ISO-9660"; + break; + case FS_TYPE_NTFS: + retval = L" NTFS"; + break; + default: + retval = L""; + break; + } // switch + return retval; } // CHAR16 *FSTypeName() +// Sets the FsName field of Volume, based on data recorded in the partition's +// filesystem. This field may remain unchanged if there's no known filesystem +// or if the name field is empty. +static VOID SetFilesystemName(REFIT_VOLUME *Volume) { + EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr = NULL; + + if ((Volume) && (Volume->RootDir != NULL)) { + FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); + } + + if ((FileSystemInfoPtr != NULL) && (FileSystemInfoPtr->VolumeLabel != NULL) && + (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { + if (Volume->FsName) { + MyFreePool(Volume->FsName); + Volume->FsName = NULL; + } + Volume->FsName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); + } + MyFreePool(FileSystemInfoPtr); +} // VOID *SetFilesystemName() + // Identify the filesystem type and record the filesystem's UUID/serial number, // if possible. Expects a Buffer containing the first few (normally at least // 4096) bytes of the filesystem. Sets the filesystem type code in Volume->FSType @@ -518,98 +635,106 @@ // GUID/UUID-manipulating functions. (As I write, it's being used merely to // detect partitions that are part of a RAID 1 array.) static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFIT_VOLUME *Volume) { - UINT32 *Ext2Incompat, *Ext2Compat; - UINT16 *Magic16; - char *MagicString; - - if ((Buffer != NULL) && (Volume != NULL)) { - SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0); - Volume->FSType = FS_TYPE_UNKNOWN; - - if (BufferSize >= (1024 + 100)) { - Magic16 = (UINT16*) (Buffer + 1024 + 56); - if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4 - Ext2Compat = (UINT32*) (Buffer + 1024 + 92); - Ext2Incompat = (UINT32*) (Buffer + 1024 + 96); - if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg - Volume->FSType = FS_TYPE_EXT4; - } else if (*Ext2Compat & 0x0004) { // check for journal - Volume->FSType = FS_TYPE_EXT3; - } else { // none of these features; presume it's ext2... - Volume->FSType = FS_TYPE_EXT2; + UINT32 *Ext2Incompat, *Ext2Compat; + UINT16 *Magic16; + char *MagicString; + + if ((Buffer != NULL) && (Volume != NULL)) { + SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0); + Volume->FSType = FS_TYPE_UNKNOWN; + + if (BufferSize >= (1024 + 100)) { + Magic16 = (UINT16*) (Buffer + 1024 + 56); + if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4 + Ext2Compat = (UINT32*) (Buffer + 1024 + 92); + Ext2Incompat = (UINT32*) (Buffer + 1024 + 96); + if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg + Volume->FSType = FS_TYPE_EXT4; + } else if (*Ext2Compat & 0x0004) { // check for journal + Volume->FSType = FS_TYPE_EXT3; + } else { // none of these features; presume it's ext2... + Volume->FSType = FS_TYPE_EXT2; + } + CopyMem(&(Volume->VolUuid), Buffer + 1024 + 104, sizeof(EFI_GUID)); + return; } - CopyMem(&(Volume->VolUuid), Buffer + 1024 + 120, sizeof(EFI_GUID)); - return; - } - } // search for ext2/3/4 magic + } // search for ext2/3/4 magic - if (BufferSize >= (65536 + 100)) { - MagicString = (char*) (Buffer + 65536 + 52); - if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) || - (CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) || - (CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) { - Volume->FSType = FS_TYPE_REISERFS; - CopyMem(&(Volume->VolUuid), Buffer + 65536 + 84, sizeof(EFI_GUID)); - return; - } // if - } // search for ReiserFS magic + if (BufferSize >= (65536 + 100)) { + MagicString = (char*) (Buffer + 65536 + 52); + if ((CompareMem(MagicString, REISERFS_SUPER_MAGIC_STRING, 8) == 0) || + (CompareMem(MagicString, REISER2FS_SUPER_MAGIC_STRING, 9) == 0) || + (CompareMem(MagicString, REISER2FS_JR_SUPER_MAGIC_STRING, 9) == 0)) { + Volume->FSType = FS_TYPE_REISERFS; + CopyMem(&(Volume->VolUuid), Buffer + 65536 + 84, sizeof(EFI_GUID)); + return; + } // if + } // search for ReiserFS magic - if (BufferSize >= (65536 + 64 + 8)) { - MagicString = (char*) (Buffer + 65536 + 64); - if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) { - Volume->FSType = FS_TYPE_BTRFS; - return; - } // if - } // search for Btrfs magic + if (BufferSize >= (65536 + 64 + 8)) { + MagicString = (char*) (Buffer + 65536 + 64); + if (CompareMem(MagicString, BTRFS_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_BTRFS; + return; + } // if + } // search for Btrfs magic - if (BufferSize >= 512) { - MagicString = (char*) Buffer; - if (CompareMem(MagicString, XFS_SIGNATURE, 4) == 0) { - Volume->FSType = FS_TYPE_XFS; - return; - } - } // search for XFS magic + if (BufferSize >= 512) { + MagicString = (char*) Buffer; + if (CompareMem(MagicString, XFS_SIGNATURE, 4) == 0) { + Volume->FSType = FS_TYPE_XFS; + return; + } + } // search for XFS magic - if (BufferSize >= (1024 + 2)) { - Magic16 = (UINT16*) (Buffer + 1024); - if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) { - Volume->FSType = FS_TYPE_HFSPLUS; - return; - } - } // search for HFS+ magic + if (BufferSize >= (32768 + 4)) { + MagicString = (char*) (Buffer + 32768); + if (CompareMem(MagicString, JFS_SIGNATURE, 4) == 0) { + Volume->FSType = FS_TYPE_JFS; + return; + } + } // search for JFS magic - if (BufferSize >= 512) { - // Search for NTFS, FAT, and MBR/EBR. - // These all have 0xAA55 at the end of the first sector, so we must - // also search for NTFS, FAT12, FAT16, and FAT32 signatures to - // figure out where to look for the filesystem serial numbers. - Magic16 = (UINT16*) (Buffer + 510); - if (*Magic16 == FAT_MAGIC) { - MagicString = (char*) Buffer; - if (CompareMem(MagicString + 3, NTFS_SIGNATURE, 8) == 0) { - Volume->FSType = FS_TYPE_NTFS; - CopyMem(&(Volume->VolUuid), Buffer + 0x48, sizeof(UINT64)); - } else if ((CompareMem(MagicString + 0x36, FAT12_SIGNATURE, 8) == 0) || - (CompareMem(MagicString + 0x36, FAT16_SIGNATURE, 8) == 0)) { - Volume->FSType = FS_TYPE_FAT; - CopyMem(&(Volume->VolUuid), Buffer + 0x27, sizeof(UINT32)); - } else if (CompareMem(MagicString + 0x52, FAT32_SIGNATURE, 8) == 0) { - Volume->FSType = FS_TYPE_FAT; - CopyMem(&(Volume->VolUuid), Buffer + 0x43, sizeof(UINT32)); - } else if (!Volume->BlockIO->Media->LogicalPartition) { - Volume->FSType = FS_TYPE_WHOLEDISK; - } // if/else - return; - } // if - } // search for FAT and NTFS magic + if (BufferSize >= (1024 + 2)) { + Magic16 = (UINT16*) (Buffer + 1024); + if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) { + Volume->FSType = FS_TYPE_HFSPLUS; + return; + } + } // search for HFS+ magic - // If no other filesystem is identified and block size is right, assume - // it's ISO-9660.... - if (Volume->BlockIO->Media->BlockSize == 2048) { - Volume->FSType = FS_TYPE_ISO9660; - return; - } - } // if ((Buffer != NULL) && (Volume != NULL)) + if (BufferSize >= 512) { + // Search for NTFS, FAT, and MBR/EBR. + // These all have 0xAA55 at the end of the first sector, so we must + // also search for NTFS, FAT12, FAT16, and FAT32 signatures to + // figure out where to look for the filesystem serial numbers. + Magic16 = (UINT16*) (Buffer + 510); + if (*Magic16 == FAT_MAGIC) { + MagicString = (char*) Buffer; + if (CompareMem(MagicString + 3, NTFS_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_NTFS; + CopyMem(&(Volume->VolUuid), Buffer + 0x48, sizeof(UINT64)); + } else if ((CompareMem(MagicString + 0x36, FAT12_SIGNATURE, 8) == 0) || + (CompareMem(MagicString + 0x36, FAT16_SIGNATURE, 8) == 0)) { + Volume->FSType = FS_TYPE_FAT; + CopyMem(&(Volume->VolUuid), Buffer + 0x27, sizeof(UINT32)); + } else if (CompareMem(MagicString + 0x52, FAT32_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_FAT; + CopyMem(&(Volume->VolUuid), Buffer + 0x43, sizeof(UINT32)); + } else if (!Volume->BlockIO->Media->LogicalPartition) { + Volume->FSType = FS_TYPE_WHOLEDISK; + } // if/else + return; + } // if + } // search for FAT and NTFS magic + + // If no other filesystem is identified and block size is right, assume + // it's ISO-9660.... + if (Volume->BlockIO->Media->BlockSize == 2048) { + Volume->FSType = FS_TYPE_ISO9660; + return; + } + } // if ((Buffer != NULL) && (Volume != NULL)) } // UINT32 SetFilesystemData() static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) @@ -634,13 +759,16 @@ Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId, Volume->BlockIOOffset, SAMPLE_SIZE, Buffer); - if (!EFI_ERROR(Status)) { + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d reading boot sector of '%s'", Status, Volume->VolName); + } else { SetFilesystemData(Buffer, SAMPLE_SIZE, Volume); } if ((Status == EFI_SUCCESS) && (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)) { - if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && (FindMem(Buffer, 512, "EXFAT", 5) == -1)) { - *Bootable = TRUE; - Volume->HasBootCode = TRUE; + if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && + (FindMem(Buffer, 512, "EXFAT", 5) == -1)) { + *Bootable = TRUE; + Volume->HasBootCode = TRUE; } // detect specific boot codes @@ -731,11 +859,9 @@ // NOTE: If you add an operating system with a name that starts with 'W' or 'L', you // need to fix AddLegacyEntry in refind/legacy.c. -#if REFIT_DEBUG > 0 - Print(L" Result of bootcode detection: %s %s (%s)\n", - Volume->HasBootCode ? L"bootable" : L"non-bootable", - Volume->OSName, Volume->OSIconName); -#endif + LOG(1, LOG_LINE_NORMAL, L"Result of bootcode detection: %s %s (%s)", + Volume->HasBootCode ? L"bootable" : L"non-bootable", Volume->OSName, + Volume->OSIconName); // dummy FAT boot sector (created by OS X's newfs_msdos) if (FindMem(Buffer, 512, "Non-system disk", 15) >= 0) @@ -764,42 +890,40 @@ } } - } else { -#if REFIT_DEBUG > 0 - CheckError(Status, L"while reading boot sector"); -#endif } } /* VOID ScanVolumeBootcode() */ // Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) { - if (Volume == NULL) - return; + if (Volume == NULL) + return; - if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES) - return; + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES) + return; + + if (Volume->VolBadgeImage == NULL) { + Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", + L".VolumeBadge", + GlobalConfig.IconSizes[ICON_SIZE_BADGE]); + } - if (Volume->VolBadgeImage == NULL) { - Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]); - } - - if (Volume->VolBadgeImage == NULL) { - switch (Volume->DiskKind) { - case DISK_KIND_INTERNAL: - Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL); - break; - case DISK_KIND_EXTERNAL: - Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL); - break; - 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() - } + if (Volume->VolBadgeImage == NULL) { + switch (Volume->DiskKind) { + case DISK_KIND_INTERNAL: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL); + break; + case DISK_KIND_EXTERNAL: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL); + break; + 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() // Return a string representing the input size in IEEE-1541 units. @@ -810,28 +934,25 @@ CHAR16 *Units = NULL, *Prefixes = L" KMGTPEZ"; CHAR16 *TheValue; - TheValue = AllocateZeroPool(sizeof(CHAR16) * 256); - if (TheValue != NULL) { - NumPrefixes = StrLen(Prefixes); - SizeInIeee = SizeInBytes; - while ((SizeInIeee > 1024) && (Index < (NumPrefixes - 1))) { - Index++; - SizeInIeee /= 1024; - } // while - if (Prefixes[Index] == ' ') { - Units = StrDuplicate(L"-byte"); - } else { - Units = StrDuplicate(L" iB"); - Units[1] = Prefixes[Index]; - } // if/else - SPrint(TheValue, 255, L"%ld%s", SizeInIeee, Units); - } // if + NumPrefixes = StrLen(Prefixes); + SizeInIeee = SizeInBytes; + while ((SizeInIeee > 1024) && (Index < (NumPrefixes - 1))) { + Index++; + SizeInIeee /= 1024; + } // while + if (Prefixes[Index] == ' ') { + Units = StrDuplicate(L"-byte"); + } else { + Units = StrDuplicate(L" iB"); + Units[1] = Prefixes[Index]; + } // if/else + TheValue = PoolPrint(L"%ld%s", SizeInIeee, Units); MyFreePool(Units); return TheValue; } // CHAR16 *SizeInIEEEUnits() // Return a name for the volume. Ideally this should be the label for the -// filesystem or volume, but this function falls back to describing the +// filesystem or partition, but this function falls back to describing the // filesystem by size (200 MiB, etc.) and/or type (ext2, HFS+, etc.), if // this information can be extracted. // The calling function is responsible for freeing the memory allocated @@ -841,52 +962,45 @@ CHAR16 *FoundName = NULL; CHAR16 *SISize, *TypeName; - if (Volume->RootDir != NULL) { - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - } - - if ((FileSystemInfoPtr != NULL) && (FileSystemInfoPtr->VolumeLabel != NULL) && - (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { - FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); + if ((Volume->FsName) && (StrLen(Volume->FsName) > 0)) { + FoundName = StrDuplicate(Volume->FsName); + LOG(3, LOG_LINE_NORMAL, L"Setting volume name to filesystem name of '%s'", FoundName); } // If no filesystem name, try to use the partition name.... - if ((FoundName == NULL) && (Volume->PartName != NULL) && (StrLen(Volume->PartName) > 0) && + if ((FoundName == NULL) && (Volume->PartName) && (StrLen(Volume->PartName) > 0) && !IsIn(Volume->PartName, IGNORE_PARTITION_NAMES)) { FoundName = StrDuplicate(Volume->PartName); + LOG(3, LOG_LINE_NORMAL, L"Setting volume name to partition name of '%s'", FoundName); } // if use partition name // No filesystem or acceptable partition name, so use fs type and size - if ((FoundName == NULL) && (FileSystemInfoPtr != NULL)) { - FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); - if (FoundName != NULL) { + if (FoundName == NULL) { + if (Volume->RootDir != NULL) { + FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); + } + if (FileSystemInfoPtr != NULL) { SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); - SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); + FoundName = PoolPrint(L"%s%s volume", SISize, FSTypeName(Volume->FSType)); MyFreePool(SISize); - } // if allocated memory OK + LOG(3, LOG_LINE_NORMAL, L"Setting volume name to filesystem description of '%s'", FoundName); + MyFreePool(FileSystemInfoPtr); + } } // if (FoundName == NULL) - MyFreePool(FileSystemInfoPtr); - if (FoundName == NULL) { - FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); - if (FoundName != NULL) { - TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant - if (StrLen(TypeName) > 0) - SPrint(FoundName, 255, L"%s volume", TypeName); - else - SPrint(FoundName, 255, L"unknown volume"); - } // if allocated memory OK + TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant + if (StrLen(TypeName) > 0) + FoundName = PoolPrint(L"%s volume", TypeName); + else + FoundName = StrDuplicate(L"unknown volume"); + LOG(3, LOG_LINE_NORMAL, L"Setting volume name to generic description of '%s'", FoundName); } // if // TODO: Above could be improved/extended, in case filesystem name is not found, // such as: // - use or add disk/partition number (e.g., "(hd0,2)") - // Desperate fallback name.... - if (FoundName == NULL) { - FoundName = StrDuplicate(L"unknown volume"); - } return FoundName; } // static CHAR16 *GetVolumeName() @@ -937,6 +1051,8 @@ return FilesFound; } // static VOID HasWindowsBiosBootFiles() +// Discover basic information about a single volume -- whether it's internal +// or external, its name, etc. VOID ScanVolume(REFIT_VOLUME *Volume) { EFI_STATUS Status; @@ -964,6 +1080,7 @@ if (EFI_ERROR(Status)) { Volume->BlockIO = NULL; Print(L"Warning: Can't get BlockIO protocol.\n"); + LOG(1, LOG_LINE_NORMAL, L"Warning: Can't get BlockIO protocol in ScanVolume()"); } else { if (Volume->BlockIO->Media->BlockSize == 2048) Volume->DiskKind = DISK_KIND_OPTICAL; @@ -993,12 +1110,6 @@ Bootable = TRUE; } -// if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH && DevicePathSubType(DevicePath) == MEDIA_VENDOR_DP) { -// Volume->IsAppleLegacy = TRUE; // legacy BIOS device entry -// // TODO: also check for Boot Camp GUID -// Bootable = FALSE; // this handle's BlockIO is just an alias for the whole device -// } - if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH) { // make a device path for the whole device PartialLength = (UINT8 *)NextDevicePath - (UINT8 *)(Volume->DevicePath); @@ -1008,62 +1119,65 @@ // get the handle for that path RemainingDevicePath = DiskDevicePath; - Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, &RemainingDevicePath, &WholeDiskHandle); + Status = refit_call3_wrapper(BS->LocateDevicePath, &BlockIoProtocol, + &RemainingDevicePath, &WholeDiskHandle); MyFreePool(DiskDevicePath); if (!EFI_ERROR(Status)) { - //Print(L" - original handle: %08x - disk handle: %08x\n", (UINT32)DeviceHandle, (UINT32)WholeDiskHandle); - // get the device path for later - Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &DevicePathProtocol, (VOID **) &DiskDevicePath); - if (!EFI_ERROR(Status)) { + Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, + &DevicePathProtocol, (VOID **) &DiskDevicePath); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Could not get DiskDevicePath for volume"); + } else { Volume->WholeDiskDevicePath = DuplicateDevicePath(DiskDevicePath); } // look at the BlockIO protocol Status = refit_call3_wrapper(BS->HandleProtocol, WholeDiskHandle, &BlockIoProtocol, (VOID **) &Volume->WholeDiskBlockIO); - if (!EFI_ERROR(Status)) { - + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Could not get WholeDiskBlockIO for volume"); + Volume->WholeDiskBlockIO = NULL; + //CheckError(Status, L"from HandleProtocol"); + } else { // check the media block size if (Volume->WholeDiskBlockIO->Media->BlockSize == 2048) Volume->DiskKind = DISK_KIND_OPTICAL; - - } else { - Volume->WholeDiskBlockIO = NULL; - //CheckError(Status, L"from HandleProtocol"); } - } //else - // CheckError(Status, L"from LocateDevicePath"); + } else { + LOG(1, LOG_LINE_NORMAL, L"Could not locate device path for volume"); + } } DevicePath = NextDevicePath; } // while - if (!Bootable) { -#if REFIT_DEBUG > 0 - if (Volume->HasBootCode) - Print(L" Volume considered non-bootable, but boot code is present\n"); -#endif - Volume->HasBootCode = FALSE; - } + if (!Bootable) { + if (Volume->HasBootCode) + LOG(2, LOG_LINE_NORMAL, L"Volume considered non-bootable, but boot code is present"); + Volume->HasBootCode = FALSE; + } - // open the root directory of the volume - Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); + // open the root directory of the volume + Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - Volume->VolName = GetVolumeName(Volume); + SetFilesystemName(Volume); + Volume->VolName = GetVolumeName(Volume); - if (Volume->RootDir == NULL) { - Volume->IsReadable = FALSE; - return; - } else { - Volume->IsReadable = TRUE; - if ((GlobalConfig.LegacyType == LEGACY_TYPE_MAC) && (Volume->FSType == FS_TYPE_NTFS) && Volume->HasBootCode) { - // VBR boot code found on NTFS, but volume is not actually bootable - // unless there are actual boot file, so check for them.... - Volume->HasBootCode = HasWindowsBiosBootFiles(Volume); - } - } // if/else + if (Volume->RootDir == NULL) { + Volume->IsReadable = FALSE; + return; + } else { + Volume->IsReadable = TRUE; + if ((GlobalConfig.LegacyType == LEGACY_TYPE_MAC) && + (Volume->FSType == FS_TYPE_NTFS) && + Volume->HasBootCode) { + // VBR boot code found on NTFS, but volume is not actually bootable + // unless there are actual boot file, so check for them.... + Volume->HasBootCode = HasWindowsBiosBootFiles(Volume); + } + } // if/else } // ScanVolume() @@ -1086,8 +1200,10 @@ WholeDiskVolume->BlockIO, WholeDiskVolume->BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer); - if (EFI_ERROR(Status)) + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d reading blocks from disk", Status); break; + } if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55) break; EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446); @@ -1108,8 +1224,7 @@ Volume->DiskKind = WholeDiskVolume->DiskKind; Volume->IsMbrPartition = TRUE; Volume->MbrPartitionIndex = LogicalPartitionIndex++; - Volume->VolName = AllocateZeroPool(256 * sizeof(UINT16)); - SPrint(Volume->VolName, 255, L"Partition %d", Volume->MbrPartitionIndex + 1); + Volume->VolName = PoolPrint(L"Partition %d", Volume->MbrPartitionIndex + 1); Volume->BlockIO = WholeDiskVolume->BlockIO; Volume->BlockIOOffset = ExtCurrent + EMbrTable[i].StartLBA; Volume->WholeDiskBlockIO = WholeDiskVolume->BlockIO; @@ -1125,6 +1240,16 @@ } // for } /* VOID ScanExtendedPartition() */ +// Scan volumes to register them in the global Volumes array, which +// includes volume names, filesystem type information, etc. +// +// NOTE: Logging in this function will be effective only when it's called +// after ReadConfig(); but ReadConfig() needs the volumes discovered in +// this function, so this function *MUST* be called first, before logging +// is activated. This function may be called a second time if drivers are +// loaded or if the user hits the Esc key to re-scan volumes, though. +// Thus, logging is not useless, but it can't be relied upon to produce +// results on the first pass. VOID ScanVolumes(VOID) { EFI_STATUS Status; @@ -1140,6 +1265,7 @@ EFI_GUID *UuidList; EFI_GUID NullUuid = NULL_GUID_VALUE; + LOG(1, LOG_LINE_SEPARATOR, L"Scanning for volumes"); MyFreePool(Volumes); Volumes = NULL; VolumesCount = 0; @@ -1147,6 +1273,7 @@ // get all filesystem handles Status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &Handles); + LOG(2, LOG_LINE_NORMAL, L"Found handles for %d volumes", HandleCount); if (Status == EFI_NOT_FOUND) { return; // no filesystems. strange, but true... } @@ -1160,6 +1287,7 @@ Volume->DeviceHandle = Handles[HandleIndex]; AddPartitionTable(Volume); ScanVolume(Volume); + LOG(1, LOG_LINE_NORMAL, L"Identified volume '%s', of type%s", Volume->VolName, FSTypeName(Volume->FSType)); if (UuidList) { UuidList[HandleIndex] = Volume->VolUuid; for (i = 0; i < HandleIndex; i++) { @@ -1243,8 +1371,7 @@ Volume->IsMbrPartition = TRUE; Volume->MbrPartitionIndex = PartitionIndex; if (Volume->VolName == NULL) { - Volume->VolName = AllocateZeroPool(sizeof(CHAR16) * 256); - SPrint(Volume->VolName, 255, L"Partition %d", PartitionIndex + 1); + Volume->VolName = PoolPrint(L"Partition %d", PartitionIndex + 1); } break; } @@ -1253,19 +1380,23 @@ MyFreePool(SectorBuffer2); } } // for + LOG(1, LOG_LINE_NORMAL, L"Identified %d volumes", VolumesCount); } /* VOID ScanVolumes() */ VOID SetVolumeIcons(VOID) { UINTN VolumeIndex; REFIT_VOLUME *Volume; + LOG(1, LOG_LINE_NORMAL, L"Setting volume icons...."); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { Volume = Volumes[VolumeIndex]; // Set volume icon based on .VolumeBadge icon or disk kind + LOG(2, LOG_LINE_NORMAL, L"Setting volume badge icon for volume %d", VolumeIndex); SetVolumeBadgeIcon(Volume); if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) { // get custom volume icons if present if (!Volume->VolIconImage) { + LOG(2, LOG_LINE_NORMAL, L"Trying to load custom volume icon image for volume %d", VolumeIndex); Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]); } } @@ -1314,12 +1445,13 @@ if (Status != EFI_BUFFER_TOO_SMALL || IterCount >= 4) break; if (BufferSize <= LastBufferSize) { - Print(L"FS Driver requests bad buffer size %d (was %d), using %d instead\n", BufferSize, LastBufferSize, LastBufferSize * 2); + LOG(1, LOG_LINE_NORMAL, + L"FS Driver requests bad buffer size %d (was %d), using %d instead", + BufferSize, LastBufferSize, LastBufferSize * 2); BufferSize = LastBufferSize * 2; -#if REFIT_DEBUG > 0 } else { - Print(L"Reallocating buffer from %d to %d\n", LastBufferSize, BufferSize); -#endif + LOG(3, LOG_LINE_NORMAL, L"Reallocating buffer from %d to %d", + LastBufferSize, BufferSize); } Buffer = EfiReallocatePool(Buffer, LastBufferSize, BufferSize); LastBufferSize = BufferSize; @@ -1373,42 +1505,36 @@ static EFI_STATUS InitializeUnicodeCollationProtocol (VOID) { - EFI_STATUS Status; + EFI_STATUS Status; + + if (mUnicodeCollation != NULL) { + return EFI_SUCCESS; + } - if (mUnicodeCollation != NULL) { - return EFI_SUCCESS; - } - - // - // BUGBUG: Proper impelmentation is to locate all Unicode Collation Protocol - // instances first and then select one which support English language. - // Current implementation just pick the first instance. - // - Status = gBS->LocateProtocol ( - &gEfiUnicodeCollation2ProtocolGuid, - NULL, - (VOID **) &mUnicodeCollation - ); - if (EFI_ERROR(Status)) { - Status = gBS->LocateProtocol ( - &gEfiUnicodeCollationProtocolGuid, - NULL, - (VOID **) &mUnicodeCollation - ); + // + // BUGBUG: Proper impelmentation is to locate all Unicode Collation Protocol + // instances first and then select one which support English language. + // Current implementation just pick the first instance. + // + Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, + NULL, (VOID **) &mUnicodeCollation); + if (EFI_ERROR(Status)) { + Status = gBS->LocateProtocol(&gEfiUnicodeCollationProtocolGuid, + NULL, (VOID **) &mUnicodeCollation); - } - return Status; + } + return Status; } static BOOLEAN MetaiMatch (IN CHAR16 *String, IN CHAR16 *Pattern) { - if (!mUnicodeCollation) { - InitializeUnicodeCollationProtocol(); - } - if (mUnicodeCollation) - return mUnicodeCollation->MetaiMatch (mUnicodeCollation, String, Pattern); - return FALSE; // Shouldn't happen + if (!mUnicodeCollation) { + InitializeUnicodeCollationProtocol(); + } + if (mUnicodeCollation) + return mUnicodeCollation->MetaiMatch (mUnicodeCollation, String, Pattern); + return FALSE; // Shouldn't happen } #endif @@ -1454,13 +1580,13 @@ EFI_STATUS DirIterClose(IN OUT REFIT_DIR_ITER *DirIter) { - if (DirIter->LastFileInfo != NULL) { - FreePool(DirIter->LastFileInfo); - DirIter->LastFileInfo = NULL; - } - if ((DirIter->CloseDirHandle) && (DirIter->DirHandle->Close)) - refit_call1_wrapper(DirIter->DirHandle->Close, DirIter->DirHandle); - return DirIter->LastStatus; + if (DirIter->LastFileInfo != NULL) { + FreePool(DirIter->LastFileInfo); + DirIter->LastFileInfo = NULL; + } + if ((DirIter->CloseDirHandle) && (DirIter->DirHandle->Close)) + refit_call1_wrapper(DirIter->DirHandle->Close, DirIter->DirHandle); + return DirIter->LastStatus; } // @@ -1485,7 +1611,7 @@ } } - return FileName; + return StrDuplicate(FileName); } // Remove the .efi extension from FileName -- for instance, if FileName is @@ -1592,24 +1718,26 @@ // string 'EFI\foo'. The calling function is responsible for // freeing the returned string's memory. CHAR16 *FindPath(IN CHAR16* FullPath) { - UINTN i, LastBackslash = 0; - CHAR16 *PathOnly = NULL; + UINTN i, LastBackslash = 0; + CHAR16 *PathOnly = NULL; - if (FullPath != NULL) { - for (i = 0; i < StrLen(FullPath); i++) { - if (FullPath[i] == '\\') - LastBackslash = i; - } // for - PathOnly = StrDuplicate(FullPath); - if (PathOnly != NULL) - PathOnly[LastBackslash] = 0; - } // if - return (PathOnly); + if (FullPath != NULL) { + for (i = 0; i < StrLen(FullPath); i++) { + if (FullPath[i] == '\\') + LastBackslash = i; + } // for + PathOnly = StrDuplicate(FullPath); + if (PathOnly != NULL) + PathOnly[LastBackslash] = 0; + } // if + return (PathOnly); } // Takes an input loadpath, splits it into disk and filename components, finds a matching // DeviceVolume, and returns that and the filename (*loader). -VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **DeviceVolume, OUT CHAR16 **loader) { +VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, + OUT REFIT_VOLUME **DeviceVolume, + OUT CHAR16 **loader) { CHAR16 *DeviceString, *VolumeDeviceString, *Temp; UINTN i = 0; BOOLEAN Found = FALSE; @@ -1723,7 +1851,7 @@ return (Found); } // static VOID FindVolume() -// Returns TRUE if Description matches Volume's VolName, PartName, or (once +// Returns TRUE if Description matches Volume's VolName, PartName, FsName or (once // transformed) PartGuid fields, FALSE otherwise (or if either pointer is NULL) BOOLEAN VolumeMatchesDescription(REFIT_VOLUME *Volume, CHAR16 *Description) { EFI_GUID TargetVolGuid = NULL_GUID_VALUE; @@ -1734,7 +1862,9 @@ TargetVolGuid = StringAsGuid(Description); return GuidsAreEqual(&TargetVolGuid, &(Volume->PartGuid)); } else { - return (MyStriCmp(Description, Volume->VolName) || MyStriCmp(Description, Volume->PartName)); + return (MyStriCmp(Description, Volume->VolName) || + MyStriCmp(Description, Volume->PartName) || + MyStriCmp(Description, Volume->FsName)); } } // BOOLEAN VolumeMatchesDescription() diff -Nru refind-0.12.0/refind/lib.h refind-0.13.2/refind/lib.h --- refind-0.12.0/refind/lib.h 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/refind/lib.h 2021-02-28 23:16:44.000000000 +0000 @@ -93,7 +93,7 @@ VOID UninitRefitLib(VOID); EFI_STATUS ReinitRefitLib(VOID); -EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size); +EFI_STATUS EfivarGetRaw(IN EFI_GUID *vendor, IN CHAR16 *name, OUT CHAR8 **buffer, OUT UINTN *size OPTIONAL); EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent); VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName); diff -Nru refind-0.12.0/refind/line_edit.c refind-0.13.2/refind/line_edit.c --- refind-0.12.0/refind/line_edit.c 2016-10-28 13:01:22.000000000 +0000 +++ refind-0.13.2/refind/line_edit.c 2021-03-07 18:29:24.000000000 +0000 @@ -34,184 +34,184 @@ static void cursor_left(UINTN *cursor, UINTN *first) { - if ((*cursor) > 0) - (*cursor)--; - else if ((*first) > 0) - (*first)--; + if ((*cursor) > 0) + (*cursor)--; + else if ((*first) > 0) + (*first)--; } static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) { - if ((*cursor)+2 < x_max) - (*cursor)++; - else if ((*first) + (*cursor) < len) - (*first)++; + if ((*cursor)+2 < x_max) + (*cursor)++; + else if ((*first) + (*cursor) < len) + (*first)++; } BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max) { - CHAR16 *line; - UINTN size; - UINTN len; - UINTN first; - UINTN y_pos = 3; - CHAR16 *print; - UINTN cursor; - BOOLEAN exit; - BOOLEAN enter; - - DrawScreenHeader(L"Line Editor"); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, (ConWidth - 71) / 2, ConHeight - 1); - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, - L"Use cursor keys to edit, Esc to exit, Enter to boot with edited options"); - - if (!line_in) - line_in = L""; - size = StrLen(line_in) + 1024; - line = AllocatePool(size * sizeof(CHAR16)); - StrCpy(line, line_in); - len = StrLen(line); - print = AllocatePool(x_max * sizeof(CHAR16)); - - refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE); - - first = 0; - cursor = 0; - enter = FALSE; - exit = FALSE; - while (!exit) { - UINTN index; - EFI_STATUS err; - EFI_INPUT_KEY key; - UINTN i; - - i = len - first; - if (i >= x_max-2) - i = x_max-2; - CopyMem(print, line + first, i * sizeof(CHAR16)); - print[i++] = ' '; - print[i] = '\0'; - - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y_pos); - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, print); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); - - refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index); - err = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key); - if (EFI_ERROR(err)) - continue; - - switch (key.ScanCode) { - case SCAN_ESC: - exit = TRUE; - break; - case SCAN_HOME: - cursor = 0; - first = 0; - continue; - case SCAN_END: - cursor = len; - if (cursor >= x_max) { - cursor = x_max-2; - first = len - (x_max-2); - } - continue; - case SCAN_UP: - while((first + cursor) && line[first + cursor] == ' ') - cursor_left(&cursor, &first); - while((first + cursor) && line[first + cursor] != ' ') - cursor_left(&cursor, &first); - while((first + cursor) && line[first + cursor] == ' ') - cursor_left(&cursor, &first); - if (first + cursor != len && first + cursor) - cursor_right(&cursor, &first, x_max, len); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); - continue; - case SCAN_DOWN: - while(line[first + cursor] && line[first + cursor] == ' ') - cursor_right(&cursor, &first, x_max, len); - while(line[first + cursor] && line[first + cursor] != ' ') - cursor_right(&cursor, &first, x_max, len); - while(line[first + cursor] && line[first + cursor] == ' ') - cursor_right(&cursor, &first, x_max, len); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); - continue; - case SCAN_RIGHT: - if (first + cursor == len) - continue; - cursor_right(&cursor, &first, x_max, len); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); - continue; - case SCAN_LEFT: - cursor_left(&cursor, &first); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); - continue; - case SCAN_DELETE: - if (len == 0) - continue; - if (first + cursor == len) - continue; - for (i = first + cursor; i < len; i++) - line[i] = line[i+1]; - line[len-1] = ' '; - len--; - continue; - } - - switch (key.UnicodeChar) { - case CHAR_LINEFEED: - case CHAR_CARRIAGE_RETURN: - *line_out = line; - line = NULL; - enter = TRUE; - exit = TRUE; - break; - case CHAR_BACKSPACE: - if (len == 0) - continue; - if (first == 0 && cursor == 0) - continue; - for (i = first + cursor-1; i < len; i++) - line[i] = line[i+1]; - len--; - if (cursor > 0) - cursor--; - if (cursor > 0 || first == 0) - continue; - /* show full line if it fits */ - if (len < x_max-2) { - cursor = first; + CHAR16 *line; + UINTN size; + UINTN len; + UINTN first; + UINTN y_pos = 3; + CHAR16 *print; + UINTN cursor; + BOOLEAN exit; + BOOLEAN enter; + + DrawScreenHeader(L"Line Editor"); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, (ConWidth - 71) / 2, ConHeight - 1); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, + L"Use cursor keys to edit, Esc to exit, Enter to boot with edited options"); + + if (!line_in) + line_in = L""; + size = StrLen(line_in) + 1024; + line = AllocatePool(size * sizeof(CHAR16)); + StrCpy(line, line_in); + len = StrLen(line); + print = AllocatePool(x_max * sizeof(CHAR16)); + + refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, TRUE); + + first = 0; + cursor = 0; + enter = FALSE; + exit = FALSE; + while (!exit) { + UINTN index; + EFI_STATUS err; + EFI_INPUT_KEY key; + UINTN i; + + i = len - first; + if (i >= x_max-2) + i = x_max-2; + CopyMem(print, line + first, i * sizeof(CHAR16)); + print[i++] = ' '; + print[i] = '\0'; + + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, y_pos); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, print); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); + + refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index); + err = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key); + if (EFI_ERROR(err)) + continue; + + switch (key.ScanCode) { + case SCAN_ESC: + exit = TRUE; + break; + case SCAN_HOME: + cursor = 0; first = 0; continue; - } - /* jump left to see what we delete */ - if (first > 10) { - first -= 10; - cursor = 10; - } else { - cursor = first; - first = 0; - } - continue; - case '\t': - case ' ' ... '~': - case 0x80 ... 0xffff: - if (len+1 == size) - continue; - for (i = len; i > first + cursor; i--) - line[i] = line[i-1]; - line[first + cursor] = key.UnicodeChar; - len++; - line[len] = '\0'; - if (cursor+2 < x_max) - cursor++; - else if (first + cursor < len) - first++; - continue; - } - } - - refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE); - MyFreePool(print); - MyFreePool(line); - return enter; + case SCAN_END: + cursor = len; + if (cursor >= x_max) { + cursor = x_max-2; + first = len - (x_max-2); + } + continue; + case SCAN_UP: + while((first + cursor) && line[first + cursor] == ' ') + cursor_left(&cursor, &first); + while((first + cursor) && line[first + cursor] != ' ') + cursor_left(&cursor, &first); + while((first + cursor) && line[first + cursor] == ' ') + cursor_left(&cursor, &first); + if (first + cursor != len && first + cursor) + cursor_right(&cursor, &first, x_max, len); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); + continue; + case SCAN_DOWN: + while(line[first + cursor] && line[first + cursor] == ' ') + cursor_right(&cursor, &first, x_max, len); + while(line[first + cursor] && line[first + cursor] != ' ') + cursor_right(&cursor, &first, x_max, len); + while(line[first + cursor] && line[first + cursor] == ' ') + cursor_right(&cursor, &first, x_max, len); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); + continue; + case SCAN_RIGHT: + if (first + cursor == len) + continue; + cursor_right(&cursor, &first, x_max, len); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); + continue; + case SCAN_LEFT: + cursor_left(&cursor, &first); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, cursor, y_pos); + continue; + case SCAN_DELETE: + if (len == 0) + continue; + if (first + cursor == len) + continue; + for (i = first + cursor; i < len; i++) + line[i] = line[i+1]; + line[len-1] = ' '; + len--; + continue; + } + + switch (key.UnicodeChar) { + case CHAR_LINEFEED: + case CHAR_CARRIAGE_RETURN: + *line_out = line; + line = NULL; + enter = TRUE; + exit = TRUE; + break; + case CHAR_BACKSPACE: + if (len == 0) + continue; + if (first == 0 && cursor == 0) + continue; + for (i = first + cursor-1; i < len; i++) + line[i] = line[i+1]; + len--; + if (cursor > 0) + cursor--; + if (cursor > 0 || first == 0) + continue; + /* show full line if it fits */ + if (len < x_max-2) { + cursor = first; + first = 0; + continue; + } + /* jump left to see what we delete */ + if (first > 10) { + first -= 10; + cursor = 10; + } else { + cursor = first; + first = 0; + } + continue; + case '\t': + case ' ' ... '~': + case 0x80 ... 0xffff: + if (len+1 == size) + continue; + for (i = len; i > first + cursor; i--) + line[i] = line[i-1]; + line[first + cursor] = key.UnicodeChar; + len++; + line[len] = '\0'; + if (cursor+2 < x_max) + cursor++; + else if (first + cursor < len) + first++; + continue; + } + } + + refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE); + MyFreePool(print); + MyFreePool(line); + return enter; } /* BOOLEAN line_edit() */ diff -Nru refind-0.12.0/refind/linux.c refind-0.13.2/refind/linux.c --- refind-0.12.0/refind/linux.c 2020-03-07 19:04:42.000000000 +0000 +++ refind-0.13.2/refind/linux.c 2021-03-09 14:15:28.000000000 +0000 @@ -61,6 +61,7 @@ #include "menu.h" #include "mystrings.h" #include "linux.h" +#include "log.h" #include "scan.h" // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath. @@ -84,8 +85,10 @@ REFIT_DIR_ITER DirIter; EFI_FILE_INFO *DirEntry; + LOG(1, LOG_LINE_NORMAL, L"Searching for an initrd to match '%s' on '%s'", LoaderPath, Volume->VolName); FileName = Basename(LoaderPath); KernelVersion = FindNumbers(FileName); + LOG(3, LOG_LINE_NORMAL, L"Kernel version string is '%s'", KernelVersion); Path = FindPath(LoaderPath); // Add trailing backslash for root directory; necessary on some systems, but must @@ -99,7 +102,7 @@ // building the InitrdName later.... if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\')) MergeStrings(&Path, L"\\", 0); - while (DirIterNext(&DirIter, 2, L"init*", &DirEntry)) { + while (DirIterNext(&DirIter, 2, L"init*,booster*", &DirEntry)) { InitrdVersion = FindNumbers(DirEntry->FileName); if (((KernelVersion != NULL) && (MyStriCmp(InitrdVersion, KernelVersion))) || ((KernelVersion == NULL) && (InitrdVersion == NULL))) { @@ -139,9 +142,10 @@ } // if DeleteStringList(InitrdNames); - // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed. MyFreePool(KernelVersion); MyFreePool(Path); + MyFreePool(FileName); + LOG(1, LOG_LINE_NORMAL, L"Located initrd is '%s'", InitrdName); return (InitrdName); } // static CHAR16 * FindInitrd() @@ -288,8 +292,10 @@ MergeStrings(&NewFile, FullName, 0); MergeStrings(&NewFile, L".efi.signed", 0); if (NewFile != NULL) { - if (FileExists(Volume->RootDir, NewFile)) + if (FileExists(Volume->RootDir, NewFile)) { + LOG(2, LOG_LINE_NORMAL, L"Found signed counterpart to '%s'", FullName); retval = TRUE; + } MyFreePool(NewFile); } // if diff -Nru refind-0.12.0/refind/log.c refind-0.13.2/refind/log.c --- refind-0.12.0/refind/log.c 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/refind/log.c 2021-03-09 18:45:14.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * refind/log.c + * + * Definitions to handle rEFInd's logging facility, activated by setting + * log_level in refind.conf. + * + */ +/* + * Copyright (c) 2012-2020 Roderick W. Smith + * + * 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 "log.h" +#include "global.h" +#include "lib.h" +#include "mystrings.h" +#include "../include/refit_call_wrapper.h" +#include "screen.h" + +// EFI_STATUS DeleteFile( + +EFI_FILE_HANDLE gLogHandle; +CHAR16 *gLogTemp = NULL; +BOOLEAN gLogActive = FALSE; + +// Open the logging file (refind.log). +// Sets the global gLogHandle variable to point to the file. +// Returns EFI_STATUS of file open operation. This might error out if rEFInd +// is installed on a read-only filesystem, for instance. +// If Restart == TRUE, then begin logging at the end of the file; +// if Restart == FALSE, then delete the file and start a new one. +EFI_STATUS StartLogging(BOOLEAN Restart) { + EFI_STATUS Status = EFI_SUCCESS; + UINT64 FileMode; + UINTN BufferSize; + EFI_FILE_HANDLE FoundEsp; + EFI_FILE_INFO *FileInfo; + UINT8 Utf16[2]; // String to hold ID for UTF-16 file start + + if (GlobalConfig.LogLevel > 0) { + if (Restart) + FileMode = EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE; + else + FileMode = EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE; + Status = refit_call5_wrapper(SelfDir->Open, SelfDir, &gLogHandle, L"refind.log", + FileMode, 0); + if (EFI_ERROR(Status)) { + Status = egFindESP(&FoundEsp); + if (!EFI_ERROR(Status)) { + Status = refit_call5_wrapper(FoundEsp->Open, FoundEsp, + &gLogHandle, L"refind.log", + FileMode, 0); + } + } + if (EFI_ERROR(Status)) { + GlobalConfig.LogLevel = 0; + PrintUglyText(L"Unable to open log file!", CENTER); + PauseForKey(); + } else { + // File has been opened; however, if it already exists, then rEFInd + // will end up writing into the existing file, so it could end up + // containing remnants of the earlier file if it was larger than + // this one needs to be. To prevent that, check the file size. If + // it's bigger than 0, delete it and start again.... + FileInfo = LibFileInfo(gLogHandle); + if ((FileInfo != NULL) && (FileInfo->FileSize > 0)) { + if (!Restart) { + Status = refit_call1_wrapper(gLogHandle->Delete, gLogHandle); + StartLogging(FALSE); + } + } else { + // UTF-16 files begin with these two bytes, so write them.... + Utf16[0] = 0xFF; + Utf16[1] = 0xFE; + BufferSize = 2; + refit_call3_wrapper(gLogHandle->Write, gLogHandle, &BufferSize, Utf16); + gLogActive = TRUE; + LOG(1, LOG_LINE_SEPARATOR, L"Beginning logging"); + } // if/else + if (Restart) { + refit_call2_wrapper(gLogHandle->SetPosition, gLogHandle, 0xFFFFFFFFFFFFFFFF); + } + gLogActive = TRUE; + } // if/else + } // if + return Status; +} // EFI_STATUS StartLogging() + +VOID StopLogging(VOID) { + if (GlobalConfig.LogLevel > 0) + refit_call1_wrapper(gLogHandle->Close, gLogHandle); // close logging file + gLogActive = FALSE; +} // VOID StopLogging() + +// Write a message (*Message) to the log file. (This pointer is freed +// and set to NULL by this function, the point being to keep these +// operations outside of the macro that calls this function.) +// LogLineType specifies the type of the log line, as specified by the +// LOG_LINE_* constants defined in log.h. +VOID WriteToLog(CHAR16 **Message, UINTN LogLineType) { + CHAR16 *TimeStr; + CHAR16 *FinalMessage = NULL; + UINTN BufferSize; + + if (gLogActive) { + switch (LogLineType) { + case LOG_LINE_SEPARATOR: + FinalMessage = PoolPrint(L"\n==========%s==========\n", *Message); + break; + case LOG_LINE_THIN_SEP: + FinalMessage = PoolPrint(L"\n----------%s----------\n", *Message); + break; + default: /* Normally LOG_LINE_NORMAL, but if there's a coding error, use this.... */ + TimeStr = GetTimeString(); + FinalMessage = PoolPrint(L"%s - %s\n", TimeStr, *Message); + if (TimeStr) + FreePool(TimeStr); + break; + } // switch + + if (FinalMessage) { + BufferSize = StrLen(FinalMessage) * 2; + refit_call3_wrapper(gLogHandle->Write, gLogHandle, &BufferSize, FinalMessage); + refit_call1_wrapper(gLogHandle->Flush, gLogHandle); + FreePool(FinalMessage); + } + } + if (*Message) { + FreePool(*Message); + *Message = NULL; + } +} // VOID WriteToLog() diff -Nru refind-0.12.0/refind/log.h refind-0.13.2/refind/log.h --- refind-0.12.0/refind/log.h 1970-01-01 00:00:00.000000000 +0000 +++ refind-0.13.2/refind/log.h 2021-02-27 04:01:39.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * refind/log.h + * + * Definitions to handle rEFInd's logging facility, activated by setting + * log_level in refind.conf. + * + */ +/* + * Copyright (c) 2012-2020 Roderick W. Smith + * + * 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. + * + */ + +#ifndef __LOG_H_ +#define __LOG_H_ + +#ifdef __MAKEWITH_GNUEFI +#include "efi.h" +#include "efilib.h" +#define EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH +#else +#include "../include/tiano_includes.h" +#endif + +extern CHAR16 *gLogTemp; + +#define LOG_LINE_NORMAL 1 +#define LOG_LINE_SEPARATOR 2 +#define LOG_LINE_THIN_SEP 3 + +// Note: gLogTemp is freed within WriteToLog() +#define LOG(level, type, ...) \ + if (level <= GlobalConfig.LogLevel) { \ + gLogTemp = PoolPrint(__VA_ARGS__); \ + WriteToLog(&gLogTemp, type); \ + } + +EFI_STATUS StartLogging(BOOLEAN Restart); +VOID StopLogging(VOID); +VOID WriteToLog(CHAR16 **Message, UINTN LogLineType); + +#endif diff -Nru refind-0.12.0/refind/main.c refind-0.13.2/refind/main.c --- refind-0.12.0/refind/main.c 2020-03-07 19:15:30.000000000 +0000 +++ refind-0.13.2/refind/main.c 2021-03-14 00:36:10.000000000 +0000 @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), or (at your option) any later version. @@ -70,8 +70,15 @@ #include "driver_support.h" #include "launch_efi.h" #include "scan.h" +#include "log.h" #include "../include/refit_call_wrapper.h" #include "../include/version.h" +#include "../libeg/efiConsoleControl.h" +#include "../libeg/efiUgaDraw.h" + +#ifndef __MAKEWITH_GNUEFI +#define LibLocateProtocol EfiLibLocateProtocol +#endif // // Some built-in menu definitions.... @@ -94,6 +101,7 @@ /* UseNvram = */ TRUE, /* ShutdownAfterTimeout = */ FALSE, /* Install = */ FALSE, + /* WriteSystemdVars = */ FALSE, /* RequestedScreenWidth = */ 0, /* RequestedScreenHeight = */ 0, /* BannerBottomEdge = */ 0, @@ -111,6 +119,7 @@ DEFAULT_BIG_ICON_SIZE, DEFAULT_MOUSE_SIZE }, /* BannerScale = */ BANNER_NOSCALE, + /* LogLevel = */ 0, /* *DiscoveredRoot = */ NULL, /* *SelfDevicePath = */ NULL, /* *BannerFileName = */ NULL, @@ -124,6 +133,7 @@ /* *DontScanDirs = */ NULL, /* *DontScanFiles = */ NULL, /* *DontScanTools = */ NULL, + /* *DontScanFirmware = */ NULL, /* *WindowsRecoveryFiles = */ NULL, /* *MacOSRecoveryFiles = */ NULL, /* *DriverDirs = */ NULL, @@ -145,18 +155,25 @@ VOID AboutrEFInd(VOID) { CHAR16 *FirmwareVendor; + CHAR16 *TempStr; UINT32 CsrStatus; + LOG(1, LOG_LINE_SEPARATOR, L"Displaying About/Info screen"); if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); AddMenuInfoLine(&AboutMenu, PoolPrint(L"rEFInd Version %s", REFIND_VERSION)); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); - AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2020 Roderick W. Smith"); + AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2021 Roderick W. Smith"); AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others"); AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Running on:"); + FirmwareVendor = StrDuplicate(ST->FirmwareVendor); + LimitStringLength(FirmwareVendor, MAX_LINE_LENGTH); // More than ~65 causes empty info page on 800x600 display + AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, + ST->FirmwareRevision >> 16, + ST->FirmwareRevision & ((1 << 16) - 1))); AddMenuInfoLine(&AboutMenu, PoolPrint(L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1))); #if defined(EFI32) AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: x86 (32 bit); Secure Boot %s", @@ -174,11 +191,9 @@ RecordgCsrStatus(CsrStatus, FALSE); AddMenuInfoLine(&AboutMenu, gCsrStatus); } - FirmwareVendor = StrDuplicate(ST->FirmwareVendor); - LimitStringLength(FirmwareVendor, MAX_LINE_LENGTH); // More than ~65 causes empty info page on 800x600 display - AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, ST->FirmwareRevision >> 16, - ST->FirmwareRevision & ((1 << 16) - 1))); - AddMenuInfoLine(&AboutMenu, PoolPrint(L" Screen Output: %s", egScreenDescription())); + TempStr = egScreenDescription(); + AddMenuInfoLine(&AboutMenu, PoolPrint(L" Screen Output: %s", TempStr)); + MyFreePool(TempStr); AddMenuInfoLine(&AboutMenu, L""); #if defined(__MAKEWITH_GNUEFI) AddMenuInfoLine(&AboutMenu, L"Built with GNU-EFI"); @@ -198,21 +213,15 @@ // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable, // if it's different from what's already stored there. VOID StoreLoaderName(IN CHAR16 *Name) { - EFI_STATUS Status; - CHAR16 *OldName = NULL; - UINTN Length; if (Name) { - Status = EfivarGetRaw(&RefindGuid, L"PreviousBoot", (CHAR8**) &OldName, &Length); - if ((Status != EFI_SUCCESS) || (StrCmp(OldName, Name) != 0)) { - EfivarSetRaw(&RefindGuid, L"PreviousBoot", (CHAR8*) Name, StrLen(Name) * 2 + 2, TRUE); - } // if - MyFreePool(OldName); + EfivarSetRaw(&RefindGuid, L"PreviousBoot", (CHAR8*) Name, StrLen(Name) * 2 + 2, TRUE); } // if } // VOID StoreLoaderName() // Rescan for boot loaders VOID RescanAll(BOOLEAN DisplayMessage, BOOLEAN Reconnect) { + LOG(1, LOG_LINE_NORMAL, L"Re-scanning all boot loaders"); FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount); MainMenu.Entries = NULL; MainMenu.EntryCount = 0; @@ -248,7 +257,9 @@ EFI_STATUS Status; BOOLEAN Success = FALSE; + LOG(1, LOG_LINE_NORMAL, L"Setting up Secure Boot (if applicable)"); if (secure_mode() && ShimLoaded()) { + LOG(2, LOG_LINE_NORMAL, L"Secure boot mode detected with loaded Shim; adding MOK extensions"); Status = security_policy_install(); if (Status == EFI_SUCCESS) { Success = TRUE; @@ -256,6 +267,8 @@ Print(L"Failed to install MOK Secure Boot extensions"); PauseForKey(); } + } else { + LOG(2, LOG_LINE_NORMAL, L"Secure boot disabled; doing nothing"); } return Success; } // VOID SecureBootSetup() @@ -316,6 +329,7 @@ CHAR16 *Element = NULL, *NewCommaDelimited = NULL, *PreviousBoot = NULL; EFI_STATUS Status; + LOG(1, LOG_LINE_NORMAL, L"Adjusting default_selection with PreviousBoot values"); while ((Element = FindCommaDelimited(GlobalConfig.DefaultSelection, i++)) != NULL) { if (MyStriCmp(Element, L"+")) { Status = EfivarGetRaw(&RefindGuid, L"PreviousBoot", (CHAR8 **) &PreviousBoot, &j); @@ -335,6 +349,92 @@ GlobalConfig.DefaultSelection = NewCommaDelimited; } // AdjustDefaultSelection() +// Log basic information (rEFInd version, EFI version, etc.) to the log file. +VOID LogBasicInfo(VOID) { + EFI_STATUS Status; + UINT64 MaximumVariableStorageSize; + UINT64 RemainingVariableStorageSize; + UINT64 MaximumVariableSize; + UINTN EfiMajorVersion = ST->Hdr.Revision >> 16; + CHAR16 *TempStr; + EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; + EFI_GUID UgaDrawProtocolGuid = EFI_UGA_DRAW_PROTOCOL_GUID; + EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + + LOG(1, LOG_LINE_SEPARATOR, L"System information"); +#if defined(__MAKEWITH_GNUEFI) + LOG(1, LOG_LINE_NORMAL, L"rEFInd %s built with GNU-EFI", REFIND_VERSION); +#else + LOG(1, LOG_LINE_NORMAL, L"rEFInd %s built with TianoCore EDK2", REFIND_VERSION); +#endif + TempStr = GuidAsString(&(SelfVolume->PartGuid)); + LOG(1, LOG_LINE_NORMAL, L"rEFInd boot partition GUID: %s", TempStr); + MyFreePool(TempStr); +#if defined(EFI32) + LOG(1, LOG_LINE_NORMAL, L"Platform: x86/IA32/i386 (32-bit)"); +#elif defined(EFIX64) + LOG(1, LOG_LINE_NORMAL, L"Platform: x86-64/X64/AMD64 (64-bit)"); +#elif defined(EFIAARCH64) + LOG(1, LOG_LINE_NORMAL, L"Platform: ARM64/AARCH64 (64-bit)"); +#else + LOG(1, LOG_LINE_NORMAL, L"Platform: unknown"); +#endif + LOG(1, LOG_LINE_NORMAL, L"Log level is %d", GlobalConfig.LogLevel); + LOG(1, LOG_LINE_NORMAL, L"Menu timeout is %d", GlobalConfig.Timeout); + LOG(1, LOG_LINE_NORMAL, L"Firmware: %s %d.%02d", ST->FirmwareVendor, + ST->FirmwareRevision >> 16, ST->FirmwareRevision & ((1 << 16) - 1)); + LOG(1, LOG_LINE_NORMAL, L"EFI Revision %d.%02d", EfiMajorVersion, + ST->Hdr.Revision & ((1 << 16) - 1)); + LOG(1, LOG_LINE_NORMAL, L"Secure Boot %s", secure_mode() ? L"active" : L"inactive"); + LOG(1, LOG_LINE_NORMAL, L"Shim is%s available", ShimLoaded() ? L"" : L" not"); + switch (GlobalConfig.LegacyType) { + case LEGACY_TYPE_MAC: + TempStr = L"CSM type: Mac"; + break; + case LEGACY_TYPE_UEFI: + TempStr = L"CSM type: UEFI"; + break; + case LEGACY_TYPE_NONE: + TempStr = L"CSM is not available"; + break; + default: // should never happen; just in case.... + TempStr = L"CSM type: unknown"; + break; + } + LOG(1, LOG_LINE_NORMAL, TempStr); + if (EfiMajorVersion > 1) { // QueryVariableInfo() is not supported in EFI 1.x + LOG(3, LOG_LINE_NORMAL, L"Trying to get variable info...."); + Status = refit_call4_wrapper(RT->QueryVariableInfo, EFI_VARIABLE_NON_VOLATILE, + &MaximumVariableStorageSize, &RemainingVariableStorageSize, + &MaximumVariableSize); + if (EFI_ERROR(Status)) { + LOG(1, LOG_LINE_NORMAL, L"Error %d; Unable to retrieve EFI variable capacity", Status); + } else { + LOG(1, LOG_LINE_NORMAL, L"EFI non-volatile storage:"); + LOG(1, LOG_LINE_NORMAL, L" Total storage: %ld", MaximumVariableStorageSize); + LOG(1, LOG_LINE_NORMAL, L" Remaining available: %ld", RemainingVariableStorageSize); + LOG(1, LOG_LINE_NORMAL, L" Maximum variable size: %ld", MaximumVariableSize); + } + } else { + LOG(1, LOG_LINE_NORMAL, L"EFI 1.x; EFI non-volatile storage information is unavailable"); + } + + // Report which video output devices are available. We don't actually + // use them, so just use TempStr as a throwaway pointer to the protocol. + Status = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **) &TempStr); + LOG(1, LOG_LINE_NORMAL, L"System does%s support text mode", + EFI_ERROR(Status) ? L" not" : L""); + + Status = LibLocateProtocol(&UgaDrawProtocolGuid, (VOID **) &TempStr); + LOG(1, LOG_LINE_NORMAL, L"System does%s support UGA Draw graphics mode", + EFI_ERROR(Status) ? L" not" : L""); + + Status = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **) &TempStr); + LOG(1, LOG_LINE_NORMAL, L"System does%s support GOP graphics mode", + EFI_ERROR(Status) ? L" not" : L""); + +} // VOID LogBasicInfo() + // // main entry point // @@ -357,22 +457,27 @@ return Status; // read configuration - CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS); + CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS); FindLegacyBootType(); if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) - CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS); + CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS); SetConfigFilename(ImageHandle); - MokProtocol = SecureBootSetup(); - // Scan volumes first to find SelfVolume, which is required by LoadDrivers(); - // however, if drivers are loaded, a second call to ScanVolumes() is needed - // to register the new filesystem(s) accessed by the drivers. - // Also, ScanVolumes() must be done before ReadConfig(), which needs - // SelfVolume->VolName. + // Scan volumes first to find SelfVolume, which is needed by LoadDrivers() + // and ReadConfig(); however, if drivers are loaded, a second call to + // ScanVolumes() is needed to register the new filesystem(s) accessed + // by the drivers. ScanVolumes(); + ReadConfig(GlobalConfig.ConfigFilename); + if (GlobalConfig.LogLevel > 0) { + StartLogging(FALSE); + LogBasicInfo(); + } + MokProtocol = SecureBootSetup(); if (LoadDrivers()) ScanVolumes(); - ReadConfig(GlobalConfig.ConfigFilename); + + LOG(1, LOG_LINE_SEPARATOR, L"Initializing basic features"); AdjustDefaultSelection(); if (GlobalConfig.SpoofOSXVersion && GlobalConfig.SpoofOSXVersion[0] != L'\0') @@ -383,6 +488,7 @@ MainMenu.TimeoutSeconds = GlobalConfig.Timeout; // disable EFI watchdog timer + LOG(1, LOG_LINE_NORMAL, L"Setting watchdog timer"); refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL); // further bootstrap (now with config available) @@ -396,8 +502,10 @@ pdInitialize(); if (GlobalConfig.ScanDelay > 0) { - if (GlobalConfig.ScanDelay > 1) + if (GlobalConfig.ScanDelay > 1) { + LOG(1, LOG_LINE_NORMAL, L"Pausing before re-scan"); egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor, CENTER); + } for (i = 0; i < GlobalConfig.ScanDelay; i++) refit_call1_wrapper(BS->Stall, 1000000); RescanAll(GlobalConfig.ScanDelay > 1, TRUE); @@ -409,6 +517,7 @@ if (GlobalConfig.ShutdownAfterTimeout) MainMenu.TimeoutText = L"Shutdown"; + LOG(1, LOG_LINE_SEPARATOR, L"Entering main loop"); while (MainLoopRunning) { MenuExit = RunMainMenu(&MainMenu, &SelectionName, &ChosenEntry); @@ -427,13 +536,17 @@ case TAG_REBOOT: // Reboot TerminateScreen(); + LOG(1, LOG_LINE_SEPARATOR, L"Rebooting system"); refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); + LOG(1, LOG_LINE_NORMAL, L"Reboot FAILED!"); MainLoopRunning = FALSE; // just in case we get this far break; case TAG_SHUTDOWN: // Shut Down TerminateScreen(); + LOG(1, LOG_LINE_SEPARATOR, L"Shutting down system"); refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL); + LOG(1, LOG_LINE_NORMAL, L"Shutdown FAILED!"); MainLoopRunning = FALSE; // just in case we get this far break; @@ -453,6 +566,10 @@ StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry, SelectionName); break; + case TAG_FIRMWARE_LOADER: // Reboot to a loader defined in the EFI UseNVRAM + RebootIntoLoader((LOADER_ENTRY *)ChosenEntry); + break; + case TAG_TOOL: // Start a EFI tool StartTool((LOADER_ENTRY *)ChosenEntry); break; @@ -492,7 +609,12 @@ // If we end up here, things have gone wrong. Try to reboot, and if that // fails, go into an endless loop. + LOG(1, LOG_LINE_SEPARATOR, L"Main loop has exited, but it should not have!"); + UninitRefitLib(); refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL); + ReinitRefitLib(); + LOG(1, LOG_LINE_SEPARATOR, L"Shutdown after main loop exit has FAILED!"); + StopLogging(); EndlessIdleLoop(); return EFI_SUCCESS; diff -Nru refind-0.12.0/refind/Makefile refind-0.13.2/refind/Makefile --- refind-0.12.0/refind/Makefile 2020-03-01 18:27:53.000000000 +0000 +++ refind-0.13.2/refind/Makefile 2021-02-27 04:01:39.000000000 +0000 @@ -38,7 +38,8 @@ OBJS = apple.o config.o crc32.o driver_support.o gpt.o icns.o \ install.o launch_efi.o launch_legacy.o lib.o line_edit.o \ - linux.o main.o menu.o mystrings.o pointer.o scan.o screen.o + linux.o log.o main.o menu.o mystrings.o pointer.o scan.o \ + screen.o include $(SRCDIR)/../Make.common diff -Nru refind-0.12.0/refind/Make.tiano refind-0.13.2/refind/Make.tiano --- refind-0.12.0/refind/Make.tiano 2020-03-01 18:27:53.000000000 +0000 +++ refind-0.13.2/refind/Make.tiano 2021-02-27 04:01:39.000000000 +0000 @@ -37,7 +37,7 @@ SOURCE_NAMES = apple AutoGen config crc32 driver_support gpt icns \ install launch_efi launch_legacy lib line_edit linux \ - main menu mystrings pointer scan screen + log main menu mystrings pointer scan screen OBJS = $(SOURCE_NAMES:=.obj) all: $(BUILDME) diff -Nru refind-0.12.0/refind/menu.c refind-0.13.2/refind/menu.c --- refind-0.12.0/refind/menu.c 2020-03-07 19:41:31.000000000 +0000 +++ refind-0.13.2/refind/menu.c 2021-03-08 03:50:07.000000000 +0000 @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), or (at your option) any later version. @@ -58,6 +58,7 @@ #include "global.h" #include "screen.h" #include "lib.h" +#include "log.h" #include "menu.h" #include "config.h" #include "libeg.h" @@ -136,7 +137,7 @@ if (TempSmallImage == NULL) TempSmallImage = egPrepareEmbeddedImage(&egemb_back_selected_small, TRUE); else - LoadedSmallImage = TRUE; + LoadedSmallImage = TRUE; SelectionImages[1] = egScaleImage(TempSmallImage, TileSizes[1], TileSizes[1]); // load big selection image @@ -145,18 +146,18 @@ } if (TempBigImage == NULL) { if (LoadedSmallImage) { - // calculate big selection image from small one - TempBigImage = egCopyImage(TempSmallImage); + // calculate big selection image from small one + TempBigImage = egCopyImage(TempSmallImage); } else { - TempBigImage = egPrepareEmbeddedImage(&egemb_back_selected_big, TRUE); + TempBigImage = egPrepareEmbeddedImage(&egemb_back_selected_big, TRUE); } } SelectionImages[0] = egScaleImage(TempBigImage, TileSizes[0], TileSizes[0]); if (TempSmallImage) - egFreeImage(TempSmallImage); + egFreeImage(TempSmallImage); if (TempBigImage) - egFreeImage(TempBigImage); + egFreeImage(TempBigImage); } // VOID InitSelection() // @@ -169,9 +170,9 @@ State->MaxIndex = (INTN)ItemCount - 1; State->FirstVisible = 0; if (AllowGraphicsMode) { - State->MaxVisible = UGAWidth / (TileSizes[0] + TILE_XSPACING) - 1; + State->MaxVisible = UGAWidth / (TileSizes[0] + TILE_XSPACING) - 1; } else - State->MaxVisible = ConHeight - 4; + State->MaxVisible = ConHeight - 4; if ((VisibleSpace > 0) && (VisibleSpace < State->MaxVisible)) State->MaxVisible = (INTN)VisibleSpace; State->PaintAll = TRUE; @@ -183,18 +184,18 @@ // Adjust variables relating to the scrolling of tags, for when a selected icon isn't // visible given the current scrolling condition.... static VOID AdjustScrollState(IN SCROLL_STATE *State) { - if (State->CurrentSelection > State->LastVisible) { - State->LastVisible = State->CurrentSelection; - State->FirstVisible = 1 + State->CurrentSelection - State->MaxVisible; - if (State->FirstVisible < 0) // shouldn't happen, but just in case.... - State->FirstVisible = 0; - State->PaintAll = TRUE; - } // Scroll forward - if (State->CurrentSelection < State->FirstVisible) { - State->FirstVisible = State->CurrentSelection; - State->LastVisible = State->CurrentSelection + State->MaxVisible - 1; - State->PaintAll = TRUE; - } // Scroll backward + if (State->CurrentSelection > State->LastVisible) { + State->LastVisible = State->CurrentSelection; + State->FirstVisible = 1 + State->CurrentSelection - State->MaxVisible; + if (State->FirstVisible < 0) // shouldn't happen, but just in case.... + State->FirstVisible = 0; + State->PaintAll = TRUE; + } // Scroll forward + if (State->CurrentSelection < State->FirstVisible) { + State->FirstVisible = State->CurrentSelection; + State->LastVisible = State->CurrentSelection + State->MaxVisible - 1; + State->PaintAll = TRUE; + } // Scroll backward } // static VOID AdjustScrollState static VOID UpdateScroll(IN OUT SCROLL_STATE *State, IN UINTN Movement) @@ -292,7 +293,7 @@ } if (State->ScrollMode == SCROLL_MODE_TEXT) - AdjustScrollState(State); + AdjustScrollState(State); if (!State->PaintAll && State->CurrentSelection != State->PreviousSelection) State->PaintSelection = TRUE; @@ -305,6 +306,7 @@ VOID AddMenuInfoLine(IN REFIT_MENU_SCREEN *Screen, IN CHAR16 *InfoLine) { + LOG(3, LOG_LINE_NORMAL, L"Adding menu info line: '%s'", InfoLine); AddListElement((VOID ***) &(Screen->InfoLines), &(Screen->InfoLineCount), InfoLine); } @@ -325,9 +327,10 @@ Shortcut[0] -= ('a' - 'A'); if (Shortcut[0]) { for (i = 0; i < Screen->EntryCount; i++) { - if (Screen->Entries[i]->ShortcutDigit == Shortcut[0] || Screen->Entries[i]->ShortcutLetter == Shortcut[0]) { - MyFreePool(Shortcut); - return i; + if (Screen->Entries[i]->ShortcutDigit == Shortcut[0] || + Screen->Entries[i]->ShortcutLetter == Shortcut[0]) { + MyFreePool(Shortcut); + return i; } // if } // for } // if @@ -369,15 +372,15 @@ // TODO: Support more sophisticated screen savers, such as power-saving // mode and dynamic images. static VOID SaveScreen(VOID) { - EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 }; + EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 }; - egClearScreen(&Black); - - WaitForInput(0); + egClearScreen(&Black); + + WaitForInput(0); - if (AllowGraphicsMode) - SwitchToGraphicsAndClear(); - ReadAllKeyStrokes(); + if (AllowGraphicsMode) + SwitchToGraphicsAndClear(); + ReadAllKeyStrokes(); } // VOID SaveScreen() // @@ -401,6 +404,7 @@ EFI_STATUS PointerStatus = EFI_NOT_READY; UINTN Item; + LOG(2, LOG_LINE_NORMAL, L"Running menu screen: '%s'", Screen->Title); if (Screen->TimeoutSeconds > 0) { HaveTimeout = TRUE; TimeoutCountdown = Screen->TimeoutSeconds * 10; @@ -657,6 +661,7 @@ if (ChosenEntry) *ChosenEntry = Screen->Entries[State.CurrentSelection]; *DefaultEntryIndex = State.CurrentSelection; + LOG(3, LOG_LINE_NORMAL, L"Returning %d from RunGenericMenu()", MenuExit); return MenuExit; } /* static UINTN RunGenericMenu() */ @@ -666,16 +671,16 @@ // Show information lines in text mode. static VOID ShowTextInfoLines(IN REFIT_MENU_SCREEN *Screen) { - INTN i; + INTN i; - BeginTextScreen(Screen->Title); - if (Screen->InfoLineCount > 0) { - refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC); - for (i = 0; i < (INTN)Screen->InfoLineCount; i++) { - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 4 + i); - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->InfoLines[i]); - } - } + BeginTextScreen(Screen->Title); + if (Screen->InfoLineCount > 0) { + refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC); + for (i = 0; i < (INTN)Screen->InfoLineCount; i++) { + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 3, 4 + i); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->InfoLines[i]); + } + } } // VOID ShowTextInfoLines() // Do most of the work for text-based menus.... @@ -749,7 +754,10 @@ ShowTextInfoLines(Screen); for (i = 0; i <= State->MaxIndex; i++) { if (i >= State->FirstVisible && i <= State->LastVisible) { - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 2, MenuPosY + (i - State->FirstVisible)); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, + ST->ConOut, + 2, + MenuPosY + (i - State->FirstVisible)); if (i == State->CurrentSelection) refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_CHOICE_CURRENT); else @@ -763,21 +771,24 @@ if (State->FirstVisible > 0) refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, ArrowUp); else - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" "); - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, MenuPosY + State->MaxVisible); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" "); + refit_call3_wrapper(ST->ConOut->SetCursorPosition, + ST->ConOut, + 0, + MenuPosY + State->MaxVisible); if (State->LastVisible < State->MaxIndex) - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, ArrowDown); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, ArrowDown); else - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" "); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, L" "); if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) { - if (Screen->Hint1 != NULL) { - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 2); - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint1); - } - if (Screen->Hint2 != NULL) { - refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 1); - refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint2); - } + if (Screen->Hint1 != NULL) { + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 2); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint1); + } + if (Screen->Hint2 != NULL) { + refit_call3_wrapper(ST->ConOut->SetCursorPosition, ST->ConOut, 0, ConHeight - 1); + refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, Screen->Hint2); + } } break; @@ -807,7 +818,6 @@ refit_call2_wrapper(ST->ConOut->OutputString, ST->ConOut, TimeoutMessage); } break; - } } @@ -816,7 +826,7 @@ // inline static UINTN TextLineHeight(VOID) { - return egGetFontHeight() + TEXT_YMARGIN * 2; + return egGetFontHeight() + TEXT_YMARGIN * 2; } // UINTN TextLineHeight() // @@ -855,16 +865,16 @@ // a single text string's background, this shouldn't be a problem, but it // may need addressing if it's applied more broadly.... static UINT8 AverageBrightness(EG_IMAGE *Image) { - UINTN i; - UINTN Sum = 0; + UINTN i; + UINTN Sum = 0; - if ((Image != NULL) && ((Image->Width * Image->Height) != 0)) { - for (i = 0; i < (Image->Width * Image->Height); i++) { - Sum += (Image->PixelData[i].r + Image->PixelData[i].g + Image->PixelData[i].b); - } - Sum /= (Image->Width * Image->Height * 3); - } // if - return (UINT8) Sum; + if ((Image != NULL) && ((Image->Width * Image->Height) != 0)) { + for (i = 0; i < (Image->Width * Image->Height); i++) { + Sum += (Image->PixelData[i].r + Image->PixelData[i].g + Image->PixelData[i].b); + } + Sum /= (Image->Width * Image->Height * 3); + } // if + return (UINT8) Sum; } // UINT8 AverageBrightness() // Display text against the screen's background image. Special case: If Text is NULL @@ -895,68 +905,70 @@ } // Compute the size & position of the window that will hold a subscreen's information. -static VOID ComputeSubScreenWindowSize(REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *XPos, UINTN *YPos, +static VOID ComputeSubScreenWindowSize(REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, + UINTN *XPos, UINTN *YPos, UINTN *Width, UINTN *Height, UINTN *LineWidth) { - UINTN i, ItemWidth, HintTop, BannerBottomEdge, TitleWidth; - UINTN FontCellWidth = egGetFontCellWidth(); - UINTN FontCellHeight = egGetFontHeight(); - - *Width = 20; - *Height = 5; - TitleWidth = egComputeTextWidth(Screen->Title); - - for (i = 0; i < Screen->InfoLineCount; i++) { - ItemWidth = StrLen(Screen->InfoLines[i]); - if (*Width < ItemWidth) { - *Width = ItemWidth; - } - (*Height)++; - } - for (i = 0; i <= State->MaxIndex; i++) { - ItemWidth = StrLen(Screen->Entries[i]->Title); - if (*Width < ItemWidth) { - *Width = ItemWidth; - } - (*Height)++; - } - *Width = (*Width + 2) * FontCellWidth; - *LineWidth = *Width; - if (Screen->TitleImage) - *Width += (Screen->TitleImage->Width + TITLEICON_SPACING * 2 + FontCellWidth); - else - *Width += FontCellWidth; - - if (*Width < TitleWidth) - *Width = TitleWidth + 2 * FontCellWidth; - - // Keep it within the bounds of the screen, or 2/3 of the screen's width - // for screens over 800 pixels wide - if (*Width > UGAWidth) - *Width = UGAWidth; - - *XPos = (UGAWidth - *Width) / 2; - - HintTop = UGAHeight - (FontCellHeight * 3); // top of hint text - *Height *= TextLineHeight(); - if (Screen->TitleImage && (*Height < (Screen->TitleImage->Height + TextLineHeight() * 4))) - *Height = Screen->TitleImage->Height + TextLineHeight() * 4; - - if (GlobalConfig.BannerBottomEdge >= HintTop) { // probably a full-screen image; treat it as an empty banner - BannerBottomEdge = 0; - } else { - BannerBottomEdge = GlobalConfig.BannerBottomEdge; - } - if (*Height > (HintTop - BannerBottomEdge - FontCellHeight * 2)) { - BannerBottomEdge = 0; - } - if (*Height > (HintTop - BannerBottomEdge - FontCellHeight * 2)) { - // TODO: Implement scrolling in text screen. - *Height = (HintTop - BannerBottomEdge - FontCellHeight * 2); - } - - *YPos = ((UGAHeight - *Height) / 2); - if (*YPos < BannerBottomEdge) - *YPos = BannerBottomEdge + FontCellHeight + (HintTop - BannerBottomEdge - *Height) / 2; + UINTN i, ItemWidth, HintTop, BannerBottomEdge, TitleWidth; + UINTN FontCellWidth = egGetFontCellWidth(); + UINTN FontCellHeight = egGetFontHeight(); + + *Width = 20; + *Height = 5; + TitleWidth = egComputeTextWidth(Screen->Title); + + for (i = 0; i < Screen->InfoLineCount; i++) { + ItemWidth = StrLen(Screen->InfoLines[i]); + if (*Width < ItemWidth) { + *Width = ItemWidth; + } + (*Height)++; + } + for (i = 0; i <= State->MaxIndex; i++) { + ItemWidth = StrLen(Screen->Entries[i]->Title); + if (*Width < ItemWidth) { + *Width = ItemWidth; + } + (*Height)++; + } + *Width = (*Width + 2) * FontCellWidth; + *LineWidth = *Width; + if (Screen->TitleImage) + *Width += (Screen->TitleImage->Width + TITLEICON_SPACING * 2 + FontCellWidth); + else + *Width += FontCellWidth; + + if (*Width < TitleWidth) + *Width = TitleWidth + 2 * FontCellWidth; + + // Keep it within the bounds of the screen, or 2/3 of the screen's width + // for screens over 800 pixels wide + if (*Width > UGAWidth) + *Width = UGAWidth; + + *XPos = (UGAWidth - *Width) / 2; + + HintTop = UGAHeight - (FontCellHeight * 3); // top of hint text + *Height *= TextLineHeight(); + if (Screen->TitleImage && (*Height < (Screen->TitleImage->Height + TextLineHeight() * 4))) + *Height = Screen->TitleImage->Height + TextLineHeight() * 4; + + if (GlobalConfig.BannerBottomEdge >= HintTop) { + // probably a full-screen image; treat it as an empty banner + BannerBottomEdge = 0; + } else { + BannerBottomEdge = GlobalConfig.BannerBottomEdge; + } + if (*Height > (HintTop - BannerBottomEdge - FontCellHeight * 2)) { + BannerBottomEdge = 0; + } + if (*Height > (HintTop - BannerBottomEdge - FontCellHeight * 2)) { + // TODO: Implement scrolling in text screen. + *Height = (HintTop - BannerBottomEdge - FontCellHeight * 2); + } + + *YPos = ((UGAHeight - *Height) / 2); + if (*YPos < BannerBottomEdge) + *YPos = BannerBottomEdge + FontCellHeight + (HintTop - BannerBottomEdge - *Height) / 2; } // VOID ComputeSubScreenWindowSize() // Displays sub-menus @@ -1005,10 +1017,13 @@ break; case MENU_FUNCTION_PAINT_ALL: - ComputeSubScreenWindowSize(Screen, State, &EntriesPosX, &EntriesPosY, &MenuWidth, &MenuHeight, &LineWidth); - DrawText(Screen->Title, FALSE, (StrLen(Screen->Title) + 2) * CharWidth, TitlePosX, EntriesPosY += TextLineHeight()); + ComputeSubScreenWindowSize(Screen, State, &EntriesPosX, &EntriesPosY, + &MenuWidth, &MenuHeight, &LineWidth); + DrawText(Screen->Title, FALSE, (StrLen(Screen->Title) + 2) * CharWidth, + TitlePosX, EntriesPosY += TextLineHeight()); if (Screen->TitleImage) { - BltImageAlpha(Screen->TitleImage, EntriesPosX + TITLEICON_SPACING, EntriesPosY + TextLineHeight() * 2, + BltImageAlpha(Screen->TitleImage, EntriesPosX + TITLEICON_SPACING, + EntriesPosY + TextLineHeight() * 2, BackgroundPixel); EntriesPosX += (Screen->TitleImage->Width + TITLEICON_SPACING * 2); } @@ -1056,90 +1071,95 @@ static VOID DrawMainMenuEntry(REFIT_MENU_ENTRY *Entry, BOOLEAN selected, UINTN XPos, UINTN YPos) { - EG_IMAGE *Background; + EG_IMAGE *Background; - // if using pointer, don't draw selection image when not hovering - if (selected && DrawSelection) { - Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, - SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); - if (Background) { - egComposeImage(Background, SelectionImages[Entry->Row], 0, 0); - BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos); - egFreeImage(Background); - } // if - } else { // Image not selected; copy background - egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos, - SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); - } // if/else + // if using pointer, don't draw selection image when not hovering + if (selected && DrawSelection) { + Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, + SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); + if (Background) { + egComposeImage(Background, SelectionImages[Entry->Row], 0, 0); + BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos); + egFreeImage(Background); + } // if + } else { // Image not selected; copy background + egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos, + SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); + } // if/else } // VOID DrawMainMenuEntry() static VOID PaintAll(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX, UINTN row0PosY, UINTN row1PosY, UINTN textPosY) { - INTN i; + INTN i; - if (Screen->Entries[State->CurrentSelection]->Row == 0) - AdjustScrollState(State); - for (i = State->FirstVisible; i <= State->MaxIndex; i++) { - if (Screen->Entries[i]->Row == 0) { - if (i <= State->LastVisible) { - DrawMainMenuEntry(Screen->Entries[i], (i == State->CurrentSelection) ? TRUE : FALSE, - itemPosX[i - State->FirstVisible], row0PosY); - } // if - } else { - DrawMainMenuEntry(Screen->Entries[i], (i == State->CurrentSelection) ? TRUE : FALSE, itemPosX[i], row1PosY); - } - } - if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL) && (!PointerActive || (PointerActive && DrawSelection))) { - DrawTextWithTransparency(L"", 0, textPosY); - DrawTextWithTransparency(Screen->Entries[State->CurrentSelection]->Title, - (UGAWidth - egComputeTextWidth(Screen->Entries[State->CurrentSelection]->Title)) >> 1, - textPosY); - } else { + if (Screen->Entries[State->CurrentSelection]->Row == 0) + AdjustScrollState(State); + for (i = State->FirstVisible; i <= State->MaxIndex; i++) { + if (Screen->Entries[i]->Row == 0) { + if (i <= State->LastVisible) { + DrawMainMenuEntry(Screen->Entries[i], (i == State->CurrentSelection) ? TRUE : FALSE, + itemPosX[i - State->FirstVisible], row0PosY); + } // if + } else { + DrawMainMenuEntry(Screen->Entries[i], + (i == State->CurrentSelection) ? TRUE : FALSE, + itemPosX[i], row1PosY); + } + } + if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL) && (!PointerActive || (PointerActive && DrawSelection))) { DrawTextWithTransparency(L"", 0, textPosY); - } + DrawTextWithTransparency(Screen->Entries[State->CurrentSelection]->Title, + (UGAWidth - egComputeTextWidth(Screen->Entries[State->CurrentSelection]->Title)) >> 1, + textPosY); + } else { + DrawTextWithTransparency(L"", 0, textPosY); + } - if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) { - DrawTextWithTransparency(Screen->Hint1, (UGAWidth - egComputeTextWidth(Screen->Hint1)) / 2, - UGAHeight - (egGetFontHeight() * 3)); - DrawTextWithTransparency(Screen->Hint2, (UGAWidth - egComputeTextWidth(Screen->Hint2)) / 2, - UGAHeight - (egGetFontHeight() * 2)); - } // if + if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) { + DrawTextWithTransparency(Screen->Hint1, (UGAWidth - egComputeTextWidth(Screen->Hint1)) / 2, + UGAHeight - (egGetFontHeight() * 3)); + DrawTextWithTransparency(Screen->Hint2, (UGAWidth - egComputeTextWidth(Screen->Hint2)) / 2, + UGAHeight - (egGetFontHeight() * 2)); + } // if } // static VOID PaintAll() // Move the selection to State->CurrentSelection, adjusting icon row if necessary... static VOID PaintSelection(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX, UINTN row0PosY, UINTN row1PosY, UINTN textPosY) { - UINTN XSelectPrev, XSelectCur, YPosPrev, YPosCur; + UINTN XSelectPrev, XSelectCur, YPosPrev, YPosCur; - if (((State->CurrentSelection <= State->LastVisible) && (State->CurrentSelection >= State->FirstVisible)) || - (State->CurrentSelection >= State->InitialRow1) ) { - if (Screen->Entries[State->PreviousSelection]->Row == 0) { - XSelectPrev = State->PreviousSelection - State->FirstVisible; - YPosPrev = row0PosY; - } else { - XSelectPrev = State->PreviousSelection; - YPosPrev = row1PosY; - } // if/else - if (Screen->Entries[State->CurrentSelection]->Row == 0) { - XSelectCur = State->CurrentSelection - State->FirstVisible; - YPosCur = row0PosY; - } else { - XSelectCur = State->CurrentSelection; - YPosCur = row1PosY; - } // if/else - DrawMainMenuEntry(Screen->Entries[State->PreviousSelection], FALSE, itemPosX[XSelectPrev], YPosPrev); - DrawMainMenuEntry(Screen->Entries[State->CurrentSelection], TRUE, itemPosX[XSelectCur], YPosCur); - if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL) && (!PointerActive || (PointerActive && DrawSelection))) { - DrawTextWithTransparency(L"", 0, textPosY); - DrawTextWithTransparency(Screen->Entries[State->CurrentSelection]->Title, - (UGAWidth - egComputeTextWidth(Screen->Entries[State->CurrentSelection]->Title)) >> 1, - textPosY); - } else { - DrawTextWithTransparency(L"", 0, textPosY); - } - } else { // Current selection not visible; must redraw the menu.... - MainMenuStyle(Screen, State, MENU_FUNCTION_PAINT_ALL, NULL); - } + if (((State->CurrentSelection <= State->LastVisible) && + (State->CurrentSelection >= State->FirstVisible)) || + (State->CurrentSelection >= State->InitialRow1) ) { + if (Screen->Entries[State->PreviousSelection]->Row == 0) { + XSelectPrev = State->PreviousSelection - State->FirstVisible; + YPosPrev = row0PosY; + } else { + XSelectPrev = State->PreviousSelection; + YPosPrev = row1PosY; + } // if/else + if (Screen->Entries[State->CurrentSelection]->Row == 0) { + XSelectCur = State->CurrentSelection - State->FirstVisible; + YPosCur = row0PosY; + } else { + XSelectCur = State->CurrentSelection; + YPosCur = row1PosY; + } // if/else + DrawMainMenuEntry(Screen->Entries[State->PreviousSelection], FALSE, + itemPosX[XSelectPrev], YPosPrev); + DrawMainMenuEntry(Screen->Entries[State->CurrentSelection], TRUE, + itemPosX[XSelectCur], YPosCur); + if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_LABEL) && (!PointerActive || (PointerActive && DrawSelection))) { + DrawTextWithTransparency(L"", 0, textPosY); + DrawTextWithTransparency(Screen->Entries[State->CurrentSelection]->Title, + (UGAWidth - egComputeTextWidth(Screen->Entries[State->CurrentSelection]->Title)) >> 1, + textPosY); + } else { + DrawTextWithTransparency(L"", 0, textPosY); + } + } else { // Current selection not visible; must redraw the menu.... + MainMenuStyle(Screen, State, MENU_FUNCTION_PAINT_ALL, NULL); + } } // static VOID MoveSelection(VOID) // Display a 48x48 icon at the specified location. Uses the image specified by @@ -1148,58 +1168,61 @@ // the icon's height. The X position is set along the icon's left // edge if Alignment == ALIGN_LEFT, and along the right edge if // Alignment == ALIGN_RIGHT -static VOID PaintIcon(IN EG_EMBEDDED_IMAGE *BuiltInIcon, IN CHAR16 *ExternalFilename, UINTN PosX, UINTN PosY, UINTN Alignment) { - EG_IMAGE *Icon = NULL; - - Icon = egFindIcon(ExternalFilename, GlobalConfig.IconSizes[ICON_SIZE_SMALL]); - if (Icon == NULL) - Icon = egPrepareEmbeddedImage(BuiltInIcon, TRUE); - if (Icon != NULL) { - if (Alignment == ALIGN_RIGHT) - PosX -= Icon->Width; - egDrawImageWithTransparency(Icon, NULL, PosX, PosY - (Icon->Height / 2), Icon->Width, Icon->Height); - egFreeImage(Icon); - } +static VOID PaintIcon(IN EG_EMBEDDED_IMAGE *BuiltInIcon, + IN CHAR16 *ExternalFilename, + UINTN PosX, UINTN PosY, UINTN Alignment) { + EG_IMAGE *Icon = NULL; + + Icon = egFindIcon(ExternalFilename, GlobalConfig.IconSizes[ICON_SIZE_SMALL]); + if (Icon == NULL) + Icon = egPrepareEmbeddedImage(BuiltInIcon, TRUE); + if (Icon != NULL) { + if (Alignment == ALIGN_RIGHT) + PosX -= Icon->Width; + egDrawImageWithTransparency(Icon, NULL, PosX, PosY - (Icon->Height / 2), Icon->Width, Icon->Height); + egFreeImage(Icon); + } } // static VOID () UINTN ComputeRow0PosY(VOID) { - return ((UGAHeight / 2) - TileSizes[0] / 2); + return ((UGAHeight / 2) - TileSizes[0] / 2); } // UINTN ComputeRow0PosY() // Display (or erase) the arrow icons to the left and right of an icon's row, // as appropriate. static VOID PaintArrows(SCROLL_STATE *State, UINTN PosX, UINTN PosY, UINTN row0Loaders) { - EG_IMAGE *TempImage; - UINTN Width, Height, RightX, AdjPosY; + EG_IMAGE *TempImage; + UINTN Width, Height, RightX, AdjPosY; - // NOTE: Assume that left and right arrows are of the same size.... - Width = egemb_arrow_left.Width; - Height = egemb_arrow_left.Height; - RightX = (UGAWidth + (TileSizes[0] + TILE_XSPACING) * State->MaxVisible) / 2 + TILE_XSPACING; - AdjPosY = PosY - (Height / 2); - - // For PaintIcon() calls, the starting Y position is moved to the midpoint - // of the surrounding row; PaintIcon() adjusts this back up by half the - // icon's height to properly center it. - if ((State->FirstVisible > 0) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) { - PaintIcon(&egemb_arrow_left, L"arrow_left", PosX, PosY, ALIGN_RIGHT); - } else { - TempImage = egCropImage(GlobalConfig.ScreenBackground, PosX - Width, AdjPosY, Width, Height); - BltImage(TempImage, PosX - Width, AdjPosY); - egFreeImage(TempImage); - } // if/else - - if ((State->LastVisible < (row0Loaders - 1)) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) { - PaintIcon(&egemb_arrow_right, L"arrow_right", RightX, PosY, ALIGN_LEFT); - } else { - TempImage = egCropImage(GlobalConfig.ScreenBackground, RightX, AdjPosY, Width, Height); - BltImage(TempImage, RightX, AdjPosY); - egFreeImage(TempImage); - } // if/else + // NOTE: Assume that left and right arrows are of the same size.... + Width = egemb_arrow_left.Width; + Height = egemb_arrow_left.Height; + RightX = (UGAWidth + (TileSizes[0] + TILE_XSPACING) * State->MaxVisible) / 2 + TILE_XSPACING; + AdjPosY = PosY - (Height / 2); + + // For PaintIcon() calls, the starting Y position is moved to the midpoint + // of the surrounding row; PaintIcon() adjusts this back up by half the + // icon's height to properly center it. + if ((State->FirstVisible > 0) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) { + PaintIcon(&egemb_arrow_left, L"arrow_left", PosX, PosY, ALIGN_RIGHT); + } else { + TempImage = egCropImage(GlobalConfig.ScreenBackground, PosX - Width, AdjPosY, Width, Height); + BltImage(TempImage, PosX - Width, AdjPosY); + egFreeImage(TempImage); + } // if/else + + if ((State->LastVisible < (row0Loaders - 1)) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) { + PaintIcon(&egemb_arrow_right, L"arrow_right", RightX, PosY, ALIGN_LEFT); + } else { + TempImage = egCropImage(GlobalConfig.ScreenBackground, RightX, AdjPosY, Width, Height); + BltImage(TempImage, RightX, AdjPosY); + egFreeImage(TempImage); + } // if/else } // VOID PaintArrows() // Display main menu in graphics mode -VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText) +VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, + IN UINTN Function, IN CHAR16 *ParamText) { INTN i; static UINTN row0PosX, row0PosXRunning, row1PosY, row0Loaders; @@ -1406,27 +1429,27 @@ // Returns TRUE if the user exited with edited options; FALSE if the user // pressed Esc to terminate the edit. static BOOLEAN EditOptions(LOADER_ENTRY *MenuEntry) { - UINTN x_max, y_max; - CHAR16 *EditedOptions; - BOOLEAN retval = FALSE; - - if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { - return FALSE; - } - - refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max); - - if (!GlobalConfig.TextOnly) - SwitchToText(TRUE); - - if (line_edit(MenuEntry->LoadOptions, &EditedOptions, x_max)) { - MyFreePool(MenuEntry->LoadOptions); - MenuEntry->LoadOptions = EditedOptions; - retval = TRUE; - } // if - if (!GlobalConfig.TextOnly) - SwitchToGraphics(); - return retval; + UINTN x_max, y_max; + CHAR16 *EditedOptions; + BOOLEAN retval = FALSE; + + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) { + return FALSE; + } + + refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max); + + if (!GlobalConfig.TextOnly) + SwitchToText(TRUE); + + if (line_edit(MenuEntry->LoadOptions, &EditedOptions, x_max)) { + MyFreePool(MenuEntry->LoadOptions); + MenuEntry->LoadOptions = EditedOptions; + retval = TRUE; + } // if + if (!GlobalConfig.TextOnly) + SwitchToGraphics(); + return retval; } // VOID EditOptions() // @@ -1440,6 +1463,7 @@ REFIT_MENU_SCREEN HideItemMenu = { NULL, NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" }; + LOG(3, LOG_LINE_NORMAL, L"Entering DisplaySimpleMessage()"); if (!Message) return; @@ -1450,6 +1474,7 @@ AddMenuInfoLine(&HideItemMenu, Message); AddMenuEntry(&HideItemMenu, &MenuEntryReturn); RunGenericMenu(&HideItemMenu, Style, &DefaultEntry, &ChosenOption); + LOG(1, LOG_LINE_NORMAL, L"%s - %s", Title, Message); } // VOID DisplaySimpleMessage() // Check each filename in FilenameList to be sure it refers to a valid file. If @@ -1487,15 +1512,28 @@ MyFreePool(OneElement); MyFreePool(Filename); MyFreePool(VolName); + VolName = NULL; DeletedSomething |= DeleteIt; } // while() return DeletedSomething; } // BOOLEAN RemoveInvalidFilenames() +// Save a list of items to be hidden to NVRAM or disk, as determined by +// GlobalConfig.UseNvram. +static VOID SaveHiddenList(IN CHAR16 *HiddenList, IN CHAR16 *VarName) { + EFI_STATUS Status; + UINTN i; + + i = HiddenList ? StrLen(HiddenList) : 0; + Status = EfivarSetRaw(&RefindGuid, VarName, (CHAR8 *) HiddenList, i * 2 + 2 * (i > 0), TRUE); + CheckError(Status, L"in SaveHiddenList()"); +} // VOID SaveHiddenList() + // Present a menu that enables the user to delete hidden tags (that is, to // un-hide them). VOID ManageHiddenTags(VOID) { - CHAR16 *AllTags = NULL, *HiddenTags, *HiddenTools, *HiddenLegacy, *OneElement = NULL; + CHAR16 *AllTags = NULL, *HiddenTags, *HiddenTools; + CHAR16 *HiddenLegacy, *HiddenFirmware, *OneElement = NULL; INTN DefaultEntry = 0; MENU_STYLE_FUNC Style = TextMenuStyle; REFIT_MENU_ENTRY *ChosenOption, *MenuEntryItem = NULL; @@ -1503,9 +1541,9 @@ L"Select an option and press Enter or", L"press Esc to return to main menu without changes" }; UINTN MenuExit, i = 0; - BOOLEAN SaveTags = FALSE, SaveTools = FALSE, SaveLegacy = FALSE; - EFI_STATUS Status; + BOOLEAN SaveTags, SaveTools, SaveLegacy = FALSE, SaveFirmware = FALSE; + LOG(1, LOG_LINE_SEPARATOR, L"Managing hidden tags"); HideItemMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_HIDDEN); if (AllowGraphicsMode) Style = GraphicsMenuStyle; @@ -1521,37 +1559,35 @@ HiddenLegacy = ReadHiddenTags(L"HiddenLegacy"); if (HiddenLegacy && (HiddenLegacy[0] != L'\0')) MergeStrings(&AllTags, HiddenLegacy, L','); + HiddenFirmware = ReadHiddenTags(L"HiddenFirmware"); + if (HiddenFirmware && (HiddenFirmware[0] != L'\0')) + MergeStrings(&AllTags, HiddenFirmware, L','); if ((AllTags) && (StrLen(AllTags) > 0)) { AddMenuInfoLine(&HideItemMenu, L"Select a tag and press Enter to restore it"); while ((OneElement = FindCommaDelimited(AllTags, i++)) != NULL) { - MenuEntryItem = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY)); + MenuEntryItem = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY)); // do not free MenuEntryItem->Title = StrDuplicate(OneElement); MenuEntryItem->Tag = TAG_RETURN; MenuEntryItem->Row = 1; AddMenuEntry(&HideItemMenu, MenuEntryItem); + MyFreePool(OneElement); } // while MenuExit = RunGenericMenu(&HideItemMenu, Style, &DefaultEntry, &ChosenOption); if (MenuExit == MENU_EXIT_ENTER) { SaveTags |= DeleteItemFromCsvList(ChosenOption->Title, HiddenTags); SaveTools |= DeleteItemFromCsvList(ChosenOption->Title, HiddenTools); - if (DeleteItemFromCsvList(ChosenOption->Title, HiddenLegacy)) { - i = HiddenLegacy ? StrLen(HiddenLegacy) : 0; - Status = EfivarSetRaw(&RefindGuid, L"HiddenLegacy", (CHAR8 *) HiddenLegacy, i * 2 + 2 * (i > 0), TRUE); - SaveLegacy = TRUE; - CheckError(Status, L"in ManageHiddenTags()"); - } // if + SaveFirmware |= DeleteItemFromCsvList(ChosenOption->Title, HiddenFirmware); + SaveLegacy |= DeleteItemFromCsvList(ChosenOption->Title, HiddenLegacy); } // if - if (SaveTags) { - i = HiddenTags ? StrLen(HiddenTags) : 0; - Status = EfivarSetRaw(&RefindGuid, L"HiddenTags", (CHAR8 *) HiddenTags, i * 2 + 2 * (i > 0), TRUE); - CheckError(Status, L"in ManageHiddenTags()"); - } - if (SaveTools) { - i = HiddenTools ? StrLen(HiddenTools) : 0; - Status = EfivarSetRaw(&RefindGuid, L"HiddenTools", (CHAR8 *) HiddenTools, i * 2 + 2 * (i > 0), TRUE); - CheckError(Status, L"in ManageHiddenTags()"); - } - if (SaveTags || SaveTools || SaveLegacy) + if (SaveTags) + SaveHiddenList(HiddenTags, L"HiddenTags"); + if (SaveLegacy) + SaveHiddenList(HiddenLegacy, L"HiddenLegacy"); + if (SaveTools) + SaveHiddenList(HiddenTools, L"HiddenTools"); + if (SaveFirmware) + SaveHiddenList(HiddenFirmware, L"HiddenFirmware"); + if (SaveTags || SaveTools || SaveLegacy || SaveFirmware) RescanAll(FALSE, FALSE); } else { DisplaySimpleMessage(L"Information", L"No hidden tags found"); @@ -1560,8 +1596,7 @@ MyFreePool(HiddenTags); MyFreePool(HiddenTools); MyFreePool(HiddenLegacy); - MyFreePool(OneElement); - MyFreePool(MenuEntryItem); + MyFreePool(HiddenFirmware); } // VOID ManageHiddenTags() CHAR16* ReadHiddenTags(CHAR16 *VarName) { @@ -1614,8 +1649,6 @@ if (Loader->Volume->VolName && (StrLen(Loader->Volume->VolName) > 0)) { FullPath = StrDuplicate(Loader->Volume->VolName); - } else if (Loader->Volume->PartName && (StrLen(Loader->Volume->PartName) > 0)) { - FullPath = StrDuplicate(Loader->Volume->PartName); } MergeStrings(&FullPath, Loader->LoaderPath, L':'); AddMenuInfoLine(HideItemMenu, PoolPrint(L"Really hide %s?", FullPath)); @@ -1634,14 +1667,35 @@ } AddToHiddenTags(VarName, FullPath); TagHidden = TRUE; + MyFreePool(GuidStr); } // if MyFreePool(FullPath); - MyFreePool(GuidStr); return TagHidden; } // BOOLEAN HideEfiTag() +static BOOLEAN HideFirmwareTag(LOADER_ENTRY *Loader, REFIT_MENU_SCREEN *HideItemMenu) { + MENU_STYLE_FUNC Style = TextMenuStyle; + REFIT_MENU_ENTRY *ChosenOption; + INTN DefaultEntry = 1; + UINTN MenuExit; + BOOLEAN TagHidden = FALSE; + + if (AllowGraphicsMode) + Style = GraphicsMenuStyle; + + AddMenuInfoLine(HideItemMenu, PoolPrint(L"Really hide '%s'?", Loader->Title)); + AddMenuEntry(HideItemMenu, &MenuEntryYes); + AddMenuEntry(HideItemMenu, &MenuEntryNo); + MenuExit = RunGenericMenu(HideItemMenu, Style, &DefaultEntry, &ChosenOption); + if (MyStriCmp(ChosenOption->Title, L"Yes") && (MenuExit == MENU_EXIT_ENTER)) { + AddToHiddenTags(L"HiddenFirmware", Loader->Title); + TagHidden = TRUE; + } // if + return TagHidden; +} // BOOLEAN HideFirmwareTag() + static BOOLEAN HideLegacyTag(LEGACY_ENTRY *LegacyLoader, REFIT_MENU_SCREEN *HideItemMenu) { MENU_STYLE_FUNC Style = TextMenuStyle; REFIT_MENU_ENTRY *ChosenOption; @@ -1707,6 +1761,11 @@ if (HideLegacyTag(LegacyLoader, &HideItemMenu)) RescanAll(FALSE, FALSE); break; + case TAG_FIRMWARE_LOADER: + HideItemMenu.Title = L"Hide Firmware Boot Option Tag"; + if (HideFirmwareTag(Loader, &HideItemMenu)) + RescanAll(FALSE, FALSE); + break; case TAG_ABOUT: case TAG_REBOOT: case TAG_SHUTDOWN: @@ -1731,6 +1790,7 @@ INTN DefaultEntry = -1; MENU_STYLE_FUNC Style = TextMenuStyle; + LOG(2, LOG_LINE_NORMAL, L"Entering RunMenu()"); if (AllowGraphicsMode) Style = GraphicsMenuStyle; @@ -1747,6 +1807,7 @@ INTN DefaultEntryIndex = -1; INTN DefaultSubmenuIndex = -1; + LOG(2, LOG_LINE_NORMAL, L"Entering RunMainMenu()"); TileSizes[0] = (GlobalConfig.IconSizes[ICON_SIZE_BIG] * 9) / 8; TileSizes[1] = (GlobalConfig.IconSizes[ICON_SIZE_SMALL] * 4) / 3; @@ -1774,7 +1835,12 @@ MenuTitle = StrDuplicate(TempChosenEntry->Title); if (MenuExit == MENU_EXIT_DETAILS) { if (TempChosenEntry->SubScreen != NULL) { - MenuExit = RunGenericMenu(TempChosenEntry->SubScreen, Style, &DefaultSubmenuIndex, &TempChosenEntry); + LOG(3, LOG_LINE_NORMAL, L"About to call RunGenericMenu() on subscreen '%s'", MenuTitle); + MenuExit = RunGenericMenu(TempChosenEntry->SubScreen, + Style, + &DefaultSubmenuIndex, + &TempChosenEntry); + LOG(3, LOG_LINE_NORMAL, L"RunGenericMenu() has returned %d", MenuExit); if (MenuExit == MENU_EXIT_ESCAPE || TempChosenEntry->Tag == TAG_RETURN) MenuExit = 0; if (MenuExit == MENU_EXIT_DETAILS) { diff -Nru refind-0.12.0/refind/mystrings.c refind-0.13.2/refind/mystrings.c --- refind-0.12.0/refind/mystrings.c 2020-03-07 19:47:03.000000000 +0000 +++ refind-0.13.2/refind/mystrings.c 2021-03-13 18:39:50.000000000 +0000 @@ -27,6 +27,7 @@ #include "mystrings.h" #include "lib.h" #include "screen.h" +#include "../include/refit_call_wrapper.h" BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) { BOOLEAN Found = 0, Terminate = 0; @@ -166,7 +167,7 @@ // Similar to MergeStrings, but breaks the input string into word chunks and // merges each word separately. Words are defined as string fragments separated -// by ' ', '_', or '-'. +// by ' ', ':', '_', or '-'. VOID MergeWords(CHAR16 **MergeTo, CHAR16 *SourceString, CHAR16 AddChar) { CHAR16 *Temp, *Word, *p; BOOLEAN LineFinished = FALSE; @@ -175,7 +176,7 @@ Temp = Word = p = StrDuplicate(SourceString); if (Temp) { while (!LineFinished) { - if ((*p == L' ') || (*p == L'_') || (*p == L'-') || (*p == L'\0')) { + if ((*p == L' ') || (*p == L':') || (*p == L'_') || (*p == L'-') || (*p == L'\0')) { if (*p == L'\0') LineFinished = TRUE; *p = L'\0'; @@ -359,36 +360,39 @@ // Returns TRUE if SmallString is an element in the comma-delimited List, // FALSE otherwise. Performs comparison case-insensitively. BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) { - UINTN i = 0; - BOOLEAN Found = FALSE; - CHAR16 *OneElement; - - if (SmallString && List) { - while (!Found && (OneElement = FindCommaDelimited(List, i++))) { - if (MyStriCmp(OneElement, SmallString)) - Found = TRUE; - MyFreePool(OneElement); - } // while - } // if - return Found; + UINTN i = 0; + BOOLEAN Found = FALSE; + CHAR16 *OneElement; + + if (SmallString && List) { + while (!Found && (OneElement = FindCommaDelimited(List, i++))) { + if (MyStriCmp(OneElement, SmallString)) + Found = TRUE; + MyFreePool(OneElement); + } // while + } // if + return Found; } // BOOLEAN IsIn() // Returns TRUE if any element of List can be found as a substring of // BigString, FALSE otherwise. Performs comparisons case-insensitively. BOOLEAN IsInSubstring(IN CHAR16 *BigString, IN CHAR16 *List) { - UINTN i = 0, ElementLength; - BOOLEAN Found = FALSE; - CHAR16 *OneElement; - - if (BigString && List) { - while (!Found && (OneElement = FindCommaDelimited(List, i++))) { - ElementLength = StrLen(OneElement); - if ((ElementLength <= StrLen(BigString)) && (StriSubCmp(OneElement, BigString))) - Found = TRUE; - MyFreePool(OneElement); - } // while - } // if - return Found; + UINTN i = 0, ElementLength; + BOOLEAN Found = FALSE; + CHAR16 *OneElement; + + if (BigString && List) { + while (!Found && (OneElement = FindCommaDelimited(List, i++))) { + ElementLength = StrLen(OneElement); + if ((ElementLength <= StrLen(BigString)) && + (ElementLength > 0) && + (StriSubCmp(OneElement, BigString))) { + Found = TRUE; + } // if + MyFreePool(OneElement); + } // while + } // if + return Found; } // BOOLEAN IsSubstringIn() // Replace *SearchString in **MainString with *ReplString -- but if *SearchString @@ -506,17 +510,17 @@ // Return the GUID as a string, suitable for display to the user. Note that the calling // function is responsible for freeing the allocated memory. CHAR16 * GuidAsString(EFI_GUID *GuidData) { - CHAR16 *TheString; + CHAR16 *TheString; - TheString = AllocateZeroPool(42 * sizeof(CHAR16)); - if (GuidData && (TheString != 0)) { - SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3, - (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2], - (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5], - (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]); - } - return TheString; + TheString = AllocateZeroPool(42 * sizeof(CHAR16)); + if (GuidData && (TheString != 0)) { + SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3, + (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2], + (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5], + (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]); + } + return TheString; } // GuidAsString(EFI_GUID *GuidData) EFI_GUID StringAsGuid(CHAR16 * InString) { @@ -541,6 +545,26 @@ return Guid; } // EFI_GUID StringAsGuid() +// Returns the current time as a string in 24-hour format; e.g., 14:03:17. +// Discards date portion, since for our purposes, we really don't care. +// Calling function is responsible for releasing returned string. +CHAR16 *GetTimeString(VOID) { + CHAR16 *TimeStr = NULL; + EFI_TIME CurrentTime; + EFI_STATUS Status = EFI_SUCCESS; + + Status = refit_call2_wrapper(ST->RuntimeServices->GetTime, &CurrentTime, NULL); + if (EFI_ERROR(Status)) { + TimeStr = PoolPrint(L"unknown time"); + } else { + TimeStr = PoolPrint(L"%02d:%02d:%02d", + CurrentTime.Hour, + CurrentTime.Minute, + CurrentTime.Second); + } + return TimeStr; +} // CHAR16 *GetTimeString() + // Delete the STRING_LIST pointed to by *StringList. VOID DeleteStringList(STRING_LIST *StringList) { STRING_LIST *Current = StringList, *Previous; diff -Nru refind-0.12.0/refind/mystrings.h refind-0.13.2/refind/mystrings.h --- refind-0.12.0/refind/mystrings.h 2020-02-19 18:28:05.000000000 +0000 +++ refind-0.13.2/refind/mystrings.h 2021-02-27 04:01:39.000000000 +0000 @@ -60,6 +60,7 @@ CHAR16 * GuidAsString(EFI_GUID *GuidData); EFI_GUID StringAsGuid(CHAR16 * InString); +CHAR16 *GetTimeString(VOID); VOID DeleteStringList(STRING_LIST *StringList); #endif diff -Nru refind-0.12.0/refind/pointer.c refind-0.13.2/refind/pointer.c --- refind-0.12.0/refind/pointer.c 2018-07-22 20:45:31.000000000 +0000 +++ refind-0.13.2/refind/pointer.c 2021-02-21 21:40:58.000000000 +0000 @@ -214,8 +214,8 @@ if(!EFI_ERROR(PointerStatus) && EFI_ERROR(Status)) { Status = EFI_SUCCESS; - INTN TargetX = 0; - INTN TargetY = 0; + INT32 TargetX = 0; + INT32 TargetY = 0; #ifdef EFI32 TargetX = State.X + (INTN)DivS64x64Remainder(SPointerState.RelativeMovementX * GlobalConfig.MouseSpeed, SPointerProtocol[Index]->Mode->ResolutionX, NULL); @@ -230,7 +230,7 @@ } else if(TargetX >= UGAWidth) { State.X = UGAWidth - 1; } else { - State.X = (UINTN)TargetX; + State.X = TargetX; } if(TargetY < 0) { @@ -238,7 +238,7 @@ } else if(TargetY >= UGAHeight) { State.Y = UGAHeight - 1; } else { - State.Y = (UINTN)TargetY; + State.Y = TargetY; } State.Holding = SPointerState.LeftButton; diff -Nru refind-0.12.0/refind/scan.c refind-0.13.2/refind/scan.c --- refind-0.12.0/refind/scan.c 2020-03-07 20:13:28.000000000 +0000 +++ refind-0.13.2/refind/scan.c 2021-03-10 03:07:37.000000000 +0000 @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2020 Roderick W. Smith + * Modifications copyright (c) 2012-2021 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), or (at your option) any later version. @@ -69,7 +69,9 @@ #include "launch_efi.h" #include "launch_legacy.h" #include "linux.h" +#include "log.h" #include "scan.h" +#include "install.h" #include "../include/refit_call_wrapper.h" @@ -154,22 +156,24 @@ NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); if ((Entry != NULL) && (NewEntry != NULL)) { - CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN)); 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) CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE)); } // if + NewEntry->InfoLineCount = Entry->InfoLineCount; NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*))); for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) { NewEntry->InfoLines[i] = (Entry->InfoLines[i]) ? StrDuplicate(Entry->InfoLines[i]) : NULL; } // for + NewEntry->EntryCount = Entry->EntryCount; 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->TimeoutSeconds = Entry->TimeoutSeconds; + NewEntry->TimeoutText = (Entry->TimeoutText) ? StrDuplicate(Entry->TimeoutText) : NULL; NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL; NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL; } // if @@ -220,12 +224,16 @@ NewEntry->Enabled = TRUE; NewEntry->UseGraphicsMode = FALSE; NewEntry->OSType = 0; + NewEntry->EfiLoaderPath = NULL; + NewEntry->EfiBootNum = 0; if (Entry != NULL) { NewEntry->LoaderPath = (Entry->LoaderPath) ? StrDuplicate(Entry->LoaderPath) : NULL; NewEntry->Volume = Entry->Volume; NewEntry->UseGraphicsMode = Entry->UseGraphicsMode; NewEntry->LoadOptions = (Entry->LoadOptions) ? StrDuplicate(Entry->LoadOptions) : NULL; NewEntry->InitrdPath = (Entry->InitrdPath) ? StrDuplicate(Entry->InitrdPath) : NULL; + NewEntry->EfiLoaderPath = (Entry->EfiLoaderPath) ? DuplicateDevicePath(Entry->EfiLoaderPath) : NULL; + NewEntry->EfiBootNum = Entry->EfiBootNum; } } // if return (NewEntry); @@ -247,13 +255,15 @@ if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry.... SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN)); if (SubScreen != NULL) { - SubScreen->Title = AllocateZeroPool(sizeof(CHAR16) * 256); - SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", - (Entry->Title != NULL) ? Entry->Title : FileName, Entry->Volume->VolName); + SubScreen->Title = PoolPrint(L"Boot Options for %s on %s", + (Entry->Title != NULL) ? Entry->Title : FileName, + Entry->Volume->VolName); + LOG(2, LOG_LINE_NORMAL, L"Creating subscreen '%s'", SubScreen->Title); SubScreen->TitleImage = Entry->me.Image; // default entry SubEntry = InitializeLoaderEntry(Entry); if (SubEntry != NULL) { + LOG(2, LOG_LINE_NORMAL, L"Creating loader entry for '%s'", SubScreen->Title); SubEntry->me.Title = StrDuplicate(L"Boot using default options"); MainOptions = SubEntry->LoadOptions; SubEntry->LoadOptions = AddInitrdToOptions(MainOptions, SubEntry->InitrdPath); @@ -480,13 +490,16 @@ PathOnly = FindPath(LoaderPath); NoExtension = StripEfiExtension(NameClues); + LOG(3, LOG_LINE_NORMAL, L"Finding loader defaults for '%s'", Entry->me.Title); 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 + LOG(4, LOG_LINE_NORMAL, L"Trying to load icon in same directory as loader...."); if (!Entry->me.Image) { - Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, GlobalConfig.IconSizes[ICON_SIZE_BIG]); + Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, + GlobalConfig.IconSizes[ICON_SIZE_BIG]); } if (!Entry->me.Image) { Entry->me.Image = egCopyImage(Volume->VolIconImage); @@ -494,6 +507,7 @@ // Begin creating icon "hints" by using last part of directory path leading // to the loader + LOG(4, LOG_LINE_NORMAL, L"Creating icon hint based on loader path '%s'", LoaderPath); Temp = FindLastDirName(LoaderPath); MergeStrings(&OSIconName, Temp, L','); MyFreePool(Temp); @@ -502,12 +516,16 @@ 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. - MergeWords(&OSIconName, Volume->VolName, L','); + // Add every "word" in the filesystem and partition names, delimited by + // spaces, dashes (-), underscores (_), or colons (:), to the list of + // hints to be used in searching for OS icons. + LOG(4, LOG_LINE_NORMAL, L"Merging hints based on filesystem name ('%s')", Volume->FsName); + MergeWords(&OSIconName, Volume->FsName, L','); + LOG(4, LOG_LINE_NORMAL, L"Merging hints based on partition name ('%s')", Volume->PartName); + MergeWords(&OSIconName, Volume->PartName, L','); } // if/else network boot + LOG(4, LOG_LINE_NORMAL, L"Adding hints based on specific loaders"); // detect specific loaders if (StriSubCmp(L"bzImage", NameClues) || StriSubCmp(L"vmlinuz", NameClues) || StriSubCmp(L"kernel", NameClues)) { if (Volume->DiskKind != DISK_KIND_NET) { @@ -568,31 +586,85 @@ if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z')) ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase Entry->me.ShortcutLetter = ShortcutLetter; - if (Entry->me.Image == NULL) + if (Entry->me.Image == NULL) { + LOG(4, LOG_LINE_NORMAL, L"Trying to locate an icon based on hints '%s'", OSIconName); Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE); + } MyFreePool(PathOnly); MyFreePool(OSIconName); MyFreePool(NoExtension); + MyFreePool(NameClues); } // VOID SetLoaderDefaults() +// Add an NVRAM-based EFI boot loader entry to the menu. +static LOADER_ENTRY * AddEfiLoaderEntry(IN EFI_DEVICE_PATH *EfiLoaderPath, + IN CHAR16 *LoaderTitle, + IN UINT16 EfiBootNum, + IN UINTN Row, + EG_IMAGE *Icon) { + LOADER_ENTRY *Entry; + CHAR16 *OSIconName = NULL; + CHAR16 *FullTitle = NULL; + CHAR16 *TempStr; + + Entry = InitializeLoaderEntry(NULL); + if (Entry) { + Entry->DiscoveryType = DISCOVERY_TYPE_AUTO; + if (LoaderTitle) + FullTitle = PoolPrint(L"Reboot to %s", LoaderTitle); + Entry->me.Title = StrDuplicate((FullTitle) ? FullTitle : L"Unknown"); + Entry->me.Row = Row; + Entry->me.Tag = TAG_FIRMWARE_LOADER; + Entry->Title = StrDuplicate((LoaderTitle) ? LoaderTitle : L"Unknown"); // without "Reboot to" + Entry->EfiLoaderPath = DuplicateDevicePath(EfiLoaderPath); + TempStr = DevicePathToStr(EfiLoaderPath); + LOG(2, LOG_LINE_NORMAL, L"EFI loader path = '%s'", TempStr); + MyFreePool(TempStr); + Entry->EfiBootNum = EfiBootNum; + MergeWords(&OSIconName, Entry->me.Title, L','); + MergeStrings(&OSIconName, L"unknown", L','); + if (Icon) { + Entry->me.Image = Icon; + } else { + Entry->me.Image = LoadOSIcon(OSIconName, NULL, FALSE); + } + if (Row == 0) { + Entry->me.BadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_EFI); + } else { + Entry->me.BadgeImage = NULL; + } + MyFreePool(OSIconName); + Entry->LoaderPath = NULL; + Entry->Volume = NULL; + Entry->LoadOptions = NULL; + Entry->InitrdPath = NULL; + Entry->Enabled = TRUE; + } // if (Entry) + AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry); + return Entry; +} // LOADER_ENTRY * AddEfiLoaderEntry() + // Add a specified EFI boot loader to the list, using automatic settings // for icons, options, etc. -static LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume, IN BOOLEAN SubScreenReturn) { +static LOADER_ENTRY * AddLoaderEntry(IN OUT CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, + IN REFIT_VOLUME *Volume, IN BOOLEAN SubScreenReturn) { LOADER_ENTRY *Entry; CleanUpPathNameSlashes(LoaderPath); Entry = InitializeLoaderEntry(NULL); - Entry->DiscoveryType = DISCOVERY_TYPE_AUTO; if (Entry != NULL) { + Entry->DiscoveryType = DISCOVERY_TYPE_AUTO; Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath); - Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256); + LOG(1, LOG_LINE_NORMAL, L"Adding loader entry for '%s'", Entry->Title); + LOG(2, LOG_LINE_NORMAL, L"Loader path is '%s'", LoaderPath); // 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). // Note: Volume->VolName will be NULL for network boot programs. if ((Volume->VolName) && (!MyStriCmp(Volume->VolName, L"Recovery HD"))) - SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName); + Entry->me.Title = PoolPrint(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.Title = PoolPrint(L"Boot %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath); Entry->me.Row = 0; Entry->me.BadgeImage = Volume->VolBadgeImage; if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) { @@ -605,6 +677,9 @@ SetLoaderDefaults(Entry, LoaderPath, Volume); GenerateSubScreen(Entry, Volume, SubScreenReturn); AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry); + LOG(3, LOG_LINE_NORMAL, L"Have successfully created menu entry for '%s'", Entry->Title); + } else { + LOG(1, LOG_LINE_NORMAL, L"Unable to initialize loader entry in AddLoaderEntry()!"); } return(Entry); @@ -684,7 +759,7 @@ BOOLEAN ScanIt = TRUE; VolGuid = GuidAsString(&(Volume->PartGuid)); - if ((IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) || + if ((IsIn(Volume->FsName, GlobalConfig.DontScanVolumes)) || (IsIn(Volume->PartName, GlobalConfig.DontScanVolumes)) || (IsIn(VolGuid, GlobalConfig.DontScanVolumes))) { MyFreePool(VolGuid); @@ -699,8 +774,9 @@ // See if Path includes an explicit volume declaration that's NOT Volume.... PathCopy = StrDuplicate(Path); if (SplitVolumeAndFilename(&PathCopy, &VolName)) { - if (VolName && !MyStriCmp(VolName, Volume->VolName)) { - ScanIt = FALSE; + if (VolName && (!MyStriCmp(VolName, Volume->FsName) || + !MyStriCmp(VolName, Volume->PartName))) { + ScanIt = FALSE; } // if } // if Path includes volume specification MyFreePool(PathCopy); @@ -827,11 +903,12 @@ EFI_STATUS Status; REFIT_DIR_ITER DirIter; EFI_FILE_INFO *DirEntry; - CHAR16 Message[256], *Extension, *FullName; + CHAR16 *Message, *Extension, *FullName; struct LOADER_LIST *LoaderList = NULL, *NewLoader; LOADER_ENTRY *FirstKernel = NULL, *LatestEntry = NULL; BOOLEAN FoundFallbackDuplicate = FALSE, IsLinux = FALSE, InSelfPath; + LOG(3, LOG_LINE_NORMAL, L"Beginning to scan directory '%s' for '%s'", Path, Pattern); InSelfPath = MyStriCmp(Path, SelfDirPath); if ((!SelfDirPath || !Path || (InSelfPath && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) || (!InSelfPath)) && (ShouldScan(Volume, Path))) { @@ -874,7 +951,8 @@ if ((FirstKernel != NULL) && IsLinux && GlobalConfig.FoldLinuxKernels) { AddKernelToSubmenu(FirstKernel, NewLoader->FileName, Volume); } else { - LatestEntry = AddLoaderEntry(NewLoader->FileName, NULL, Volume, !(IsLinux && GlobalConfig.FoldLinuxKernels)); + LatestEntry = AddLoaderEntry(NewLoader->FileName, NULL, Volume, + !(IsLinux && GlobalConfig.FoldLinuxKernels)); if (IsLinux && (FirstKernel == NULL)) FirstKernel = LatestEntry; } @@ -891,13 +969,15 @@ // it down to buggy EFI implementations and ignoring that particular error.... if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) { if (Path) - SPrint(Message, 255, L"while scanning the %s directory on %s", Path, Volume->VolName); + Message = PoolPrint(L"while scanning the %s directory on %s", Path, Volume->VolName); else - SPrint(Message, 255, L"while scanning the root directory on %s", Path, Volume->VolName); + Message = PoolPrint(L"while scanning the root directory on %s", Path, Volume->VolName); CheckError(Status, Message); + MyFreePool(Message); } // if (Status != EFI_NOT_FOUND) } // if not scanning a blacklisted directory + LOG(3, LOG_LINE_NORMAL, L"Done scanning directory '%s' for '%s'", Path, Pattern); return FoundFallbackDuplicate; } /* static VOID ScanLoaderDir() */ @@ -930,6 +1010,7 @@ CHAR16 *Location; REFIT_VOLUME *NetVolume; + LOG(1, LOG_LINE_NORMAL, L"Scanning for iPXE boot options"); if (FileExists(SelfVolume->RootDir, IPXE_DISCOVER_NAME) && FileExists(SelfVolume->RootDir, IPXE_NAME) && IsValidLoader(SelfVolume->RootDir, IPXE_DISCOVER_NAME) && @@ -940,9 +1021,8 @@ CopyMem(NetVolume, SelfVolume, sizeof(REFIT_VOLUME)); NetVolume->DiskKind = DISK_KIND_NET; NetVolume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET); - NetVolume->PartName = NetVolume->VolName = NULL; + NetVolume->PartName = NetVolume->VolName = NetVolume->FsName = NULL; AddLoaderEntry(iPXEFileName, Location, NetVolume, TRUE); - MyFreePool(NetVolume); } // if support files exist and are valid } } // VOID ScanNetboot() @@ -970,44 +1050,51 @@ EFI_STATUS Status; REFIT_DIR_ITER EfiDirIter; EFI_FILE_INFO *EfiDirEntry; - CHAR16 FileName[256], *Directory = NULL, *MatchPatterns, *VolName = NULL, *SelfPath, *Temp; + CHAR16 *FileName, *Directory = NULL, *MatchPatterns, *VolName = NULL, *SelfPath, *Temp; UINTN i, Length; BOOLEAN ScanFallbackLoader = TRUE; BOOLEAN FoundBRBackup = FALSE; if (Volume && (Volume->RootDir != NULL) && (Volume->VolName != NULL) && (Volume->IsReadable)) { + LOG(1, LOG_LINE_NORMAL, L"Scanning EFI files on %s", + Volume->PartName ? Volume->PartName : Volume->VolName); MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS); if (GlobalConfig.ScanAllLinux) MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L','); // check for macOS boot loader if (ShouldScan(Volume, MACOSX_LOADER_DIR)) { - StrCpy(FileName, MACOSX_LOADER_PATH); + FileName = StrDuplicate(MACOSX_LOADER_PATH); ScanFallbackLoader &= ScanMacOsLoader(Volume, FileName); + MyFreePool(FileName); DirIterOpen(Volume->RootDir, L"\\", &EfiDirIter); while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) { if (IsGuid(EfiDirEntry->FileName)) { - SPrint(FileName, 255, L"%s\\%s", EfiDirEntry->FileName, MACOSX_LOADER_PATH); + FileName = PoolPrint(L"%s\\%s", EfiDirEntry->FileName, MACOSX_LOADER_PATH); ScanFallbackLoader &= ScanMacOsLoader(Volume, FileName); - SPrint(FileName, 255, L"%s\\%s", EfiDirEntry->FileName, L"boot.efi"); + MyFreePool(FileName); + FileName = PoolPrint(L"%s\\%s", EfiDirEntry->FileName, L"boot.efi"); if (!StriSubCmp(FileName, GlobalConfig.MacOSRecoveryFiles)) MergeStrings(&GlobalConfig.MacOSRecoveryFiles, FileName, L','); + MyFreePool(FileName); } // if } // while Status = DirIterClose(&EfiDirIter); // check for XOM - StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi"); - if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, MACOSX_LOADER_DIR, L"xom.efi", GlobalConfig.DontScanFiles)) { + FileName = StrDuplicate(L"System\\Library\\CoreServices\\xom.efi"); + if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, MACOSX_LOADER_DIR, L"xom.efi", + GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume, TRUE); if (DuplicatesFallback(Volume, FileName)) ScanFallbackLoader = FALSE; } + MyFreePool(FileName); } // if should scan Mac directory // check for Microsoft boot loader/menu if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) { - StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi"); + FileName = StrDuplicate(L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi"); if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, L"EFI\\Microsoft\\Boot", L"bkpbootmgfw.efi", GlobalConfig.DontScanFiles)) { AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume, TRUE); @@ -1015,7 +1102,8 @@ if (DuplicatesFallback(Volume, FileName)) ScanFallbackLoader = FALSE; } - StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi"); + MyFreePool(FileName); + FileName = StrDuplicate(L"EFI\\Microsoft\\Boot\\bootmgfw.efi"); if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, L"EFI\\Microsoft\\Boot", L"bootmgfw.efi", GlobalConfig.DontScanFiles)) { if (FoundBRBackup) @@ -1025,6 +1113,7 @@ if (DuplicatesFallback(Volume, FileName)) ScanFallbackLoader = FALSE; } + MyFreePool(FileName); } // if // scan the root directory for EFI executables @@ -1036,9 +1125,10 @@ while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) { if (MyStriCmp(EfiDirEntry->FileName, L"tools") || EfiDirEntry->FileName[0] == '.') continue; // skip this, doesn't contain boot loaders or is scanned later - SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName); + FileName = PoolPrint(L"EFI\\%s", EfiDirEntry->FileName); if (ScanLoaderDir(Volume, FileName, MatchPatterns)) ScanFallbackLoader = FALSE; + MyFreePool(FileName); } // while() Status = DirIterClose(&EfiDirIter); if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) { @@ -1075,13 +1165,16 @@ AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume, TRUE); } MyFreePool(MatchPatterns); - } // if + } else { + LOG(1, LOG_LINE_NORMAL, L"Called ScanEfiFiles() on an invalid volume"); + } } // static VOID ScanEfiFiles() // Scan internal disks for valid EFI boot loaders.... static VOID ScanInternal(VOID) { UINTN VolumeIndex; + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for internal EFI-mode boot loaders"); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) { ScanEfiFiles(Volumes[VolumeIndex]); @@ -1093,6 +1186,7 @@ static VOID ScanExternal(VOID) { UINTN VolumeIndex; + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for external EFI-mode boot loaders"); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) { ScanEfiFiles(Volumes[VolumeIndex]); @@ -1104,6 +1198,7 @@ static VOID ScanOptical(VOID) { UINTN VolumeIndex; + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for bootable EFI-mode optical discs"); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) { ScanEfiFiles(Volumes[VolumeIndex]); @@ -1111,6 +1206,63 @@ } // for } // static VOID ScanOptical() +// Scan options stored in EFI firmware's boot list. Adds discovered and allowed +// items to the specified Row. +// If MatchThis != NULL, only adds items with labels containing any element of +// the MatchThis comma-delimited string; otherwise, searches for anything that +// doesn't match GlobalConfig.DontScanFirmware or the contents of the +// HiddenFirmware EFI variable. +// If Icon != NULL, uses the specified icon; otherwise tries to find one to +// match the label. +static VOID ScanFirmwareDefined(IN UINTN Row, IN CHAR16 *MatchThis, IN EG_IMAGE *Icon) { + BOOT_ENTRY_LIST *BootEntries, *CurrentEntry; + BOOLEAN ScanIt = TRUE; + CHAR16 *OneElement = NULL; + CHAR16 *DontScanFirmware; + UINTN i; + + if (Row == 0) + LOG(1, LOG_LINE_THIN_SEP, L"Scanning for EFI firmware-defined boot options"); + DontScanFirmware = ReadHiddenTags(L"HiddenFirmware"); + LOG(2, LOG_LINE_NORMAL, L"GlobalConfig.DontScanFirmware = '%s'", GlobalConfig.DontScanFirmware); + LOG(2, LOG_LINE_NORMAL, L"Firmware hidden tags = '%s'", DontScanFirmware); + MergeStrings(&DontScanFirmware, GlobalConfig.DontScanFirmware, L','); + if (Row == 0) { + LOG(2, LOG_LINE_NORMAL, L"Also not scanning for shells"); + MergeStrings(&DontScanFirmware, L"shell", L','); + } + LOG(3, LOG_LINE_NORMAL, L"Merged firmware don't-scan list is '%s'", DontScanFirmware); + BootEntries = FindBootOrderEntries(); + CurrentEntry = BootEntries; + while (CurrentEntry != NULL) { + if (MatchThis) { + ScanIt = FALSE; + i = 0; + while (!ScanIt && (OneElement = FindCommaDelimited(MatchThis, i++))) { + if (StriSubCmp(OneElement, CurrentEntry->BootEntry.Label) && + !IsInSubstring(CurrentEntry->BootEntry.Label, DontScanFirmware)) { + ScanIt = TRUE; + } + MyFreePool(OneElement); + } // while() + } else { + if (IsInSubstring(CurrentEntry->BootEntry.Label, DontScanFirmware)) { + ScanIt = FALSE; + } + } // if/else + if (ScanIt) { + LOG(1, LOG_LINE_NORMAL, L"Adding EFI loader entry for '%s'", CurrentEntry->BootEntry.Label); + AddEfiLoaderEntry(CurrentEntry->BootEntry.DevPath, + CurrentEntry->BootEntry.Label, + CurrentEntry->BootEntry.BootNum, Row, Icon); + } + CurrentEntry = CurrentEntry->NextBootEntry; + ScanIt = TRUE; // Assume the next item is to be scanned + } // while() + MyFreePool(DontScanFirmware); + DeleteBootOrderEntries(BootEntries); +} // static VOID ScanFirmwareDefined() + // default volume badge icon based on disk kind EG_IMAGE * GetDiskBadge(IN UINTN DiskType) { EG_IMAGE * Badge = NULL; @@ -1162,7 +1314,9 @@ BOOLEAN ScanForLegacy = FALSE; EG_PIXEL BGColor = COLOR_LIGHTBLUE; CHAR16 *HiddenTags; + CHAR16 *OrigDontScanFiles, *OrigDontScanVolumes; + LOG(1, LOG_LINE_SEPARATOR, L"Scanning for boot loaders"); if (ShowMessage) egDisplayMessage(L"Scanning for boot loaders; please wait....", &BGColor, CENTER); @@ -1179,6 +1333,19 @@ BdsAddNonExistingLegacyBootOptions(); } // if + // We temporarily modify GlobalConfig.DontScanFiles and GlobalConfig.DontScanVolumes + // to include contents of EFI HiddenTags and HiddenLegacy variables so that we don't + // have to re-load these EFI variables in several functions called from this one. + // To do this, we must be able to restore the original contents, so back them up + // first. + // We do *NOT* do this with GlobalConfig.DontScanFirmware and + // GlobalConfig.DontScanTools variables because they're used in only one function + // each, so it's easier to create a temporary variable for the merged contents + // there and not modify the global variable. + OrigDontScanFiles = StrDuplicate(GlobalConfig.DontScanFiles); + OrigDontScanVolumes = StrDuplicate(GlobalConfig.DontScanVolumes); + + // Add hidden tags to two GlobalConfig.DontScan* variables.... HiddenTags = ReadHiddenTags(L"HiddenTags"); if ((HiddenTags) && (StrLen(HiddenTags) > 0)) { MergeStrings(&GlobalConfig.DontScanFiles, HiddenTags, L','); @@ -1187,7 +1354,6 @@ if ((HiddenTags) && (StrLen(HiddenTags) > 0)) { MergeStrings(&GlobalConfig.DontScanVolumes, HiddenTags, L','); } - MyFreePool(HiddenTags); // scan for loaders and tools, add them to the menu for (i = 0; i < NUM_SCAN_OPTIONS; i++) { @@ -1216,28 +1382,43 @@ case 'n': case 'N': ScanNetboot(); break; + case 'f': case 'F': + ScanFirmwareDefined(0, NULL, NULL); + break; } // switch() } // for + // Restore the backed-up GlobalConfig.DontScan* variables.... + MyFreePool(GlobalConfig.DontScanFiles); + GlobalConfig.DontScanFiles = OrigDontScanFiles; + MyFreePool(GlobalConfig.DontScanVolumes); + GlobalConfig.DontScanVolumes = OrigDontScanVolumes; + // assign shortcut keys + LOG(2, LOG_LINE_NORMAL, L"Assigning boot shortcut keys"); for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++) MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i); // wait for user ACK when there were errors - FinishTextScreen(FALSE); +// SwitchToText(FALSE); +// FinishTextScreen(FALSE); } // VOID ScanForBootloaders() // Checks to see if a specified file seems to be a valid tool. // Returns TRUE if it passes all tests, FALSE otherwise static BOOLEAN IsValidTool(IN REFIT_VOLUME *BaseVolume, CHAR16 *PathName) { CHAR16 *DontVolName = NULL, *DontPathName = NULL, *DontFileName = NULL, *DontScanThis; - CHAR16 *TestVolName = NULL, *TestPathName = NULL, *TestFileName = NULL; + CHAR16 *TestVolName = NULL, *TestPathName = NULL, *TestFileName = NULL, *DontScanTools; BOOLEAN retval = TRUE; UINTN i = 0; + LOG(3, LOG_LINE_NORMAL, L"Checking validity of tool '%s' on '%s'", PathName, + BaseVolume->PartName ? BaseVolume->PartName : BaseVolume->VolName); + DontScanTools = ReadHiddenTags(L"HiddenTools"); + MergeStrings(&DontScanTools, GlobalConfig.DontScanTools, L','); if (FileExists(BaseVolume->RootDir, PathName) && IsValidLoader(BaseVolume->RootDir, PathName)) { SplitPathName(PathName, &TestVolName, &TestPathName, &TestFileName); - while (retval && (DontScanThis = FindCommaDelimited(GlobalConfig.DontScanTools, i++))) { + while (retval && (DontScanThis = FindCommaDelimited(DontScanTools, i++))) { SplitPathName(DontScanThis, &DontVolName, &DontPathName, &DontFileName); if (MyStriCmp(TestFileName, DontFileName) && ((DontPathName == NULL) || (MyStriCmp(TestPathName, DontPathName))) && @@ -1245,12 +1426,21 @@ retval = FALSE; } // if MyFreePool(DontScanThis); + MyFreePool(DontVolName); + DontVolName = NULL; + MyFreePool(DontPathName); + DontPathName = NULL; + MyFreePool(DontFileName); + DontFileName = NULL; } // while } else retval = FALSE; MyFreePool(TestVolName); MyFreePool(TestPathName); MyFreePool(TestFileName); + MyFreePool(DontScanTools); + LOG(4, LOG_LINE_NORMAL, L"Done checking validity of tool '%s' on '%s'", PathName, + BaseVolume->PartName ? BaseVolume->PartName : BaseVolume->VolName); return retval; } // VOID IsValidTool() @@ -1258,7 +1448,7 @@ // specified Names and add it to the menu. static VOID FindTool(CHAR16 *Locations, CHAR16 *Names, CHAR16 *Description, UINTN Icon) { UINTN j = 0, k, VolumeIndex; - CHAR16 *DirName, *FileName, *PathName, FullDescription[256]; + CHAR16 *DirName, *FileName, *PathName, *FullDescription; while ((DirName = FindCommaDelimited(Locations, j++)) != NULL) { k = 0; @@ -1267,8 +1457,13 @@ MergeStrings(&PathName, FileName, MyStriCmp(PathName, L"\\") ? 0 : L'\\'); for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) { if ((Volumes[VolumeIndex]->RootDir != NULL) && (IsValidTool(Volumes[VolumeIndex], PathName))) { - SPrint(FullDescription, 255, L"%s at %s on %s", Description, PathName, Volumes[VolumeIndex]->VolName); - AddToolEntry(Volumes[VolumeIndex], PathName, FullDescription, BuiltinIcon(Icon), 'S', FALSE); + FullDescription = PoolPrint(L"%s at %s on %s", Description, PathName, + Volumes[VolumeIndex]->VolName); + LOG(1, LOG_LINE_NORMAL, L"Adding tag for '%s' on '%s'", FileName, + Volumes[VolumeIndex]->VolName); + AddToolEntry(Volumes[VolumeIndex], PathName, FullDescription, + BuiltinIcon(Icon), 'S', FALSE); + MyFreePool(FullDescription); } // if } // for MyFreePool(PathName); @@ -1281,47 +1476,46 @@ // Add the second-row tags containing built-in and external tools (EFI shell, // reboot, etc.) VOID ScanForTools(VOID) { - CHAR16 *FileName = NULL, *VolName = NULL, *MokLocations, Description[256], *HiddenTools; + CHAR16 *FileName = NULL, *VolName = NULL, *MokLocations, *Description; REFIT_MENU_ENTRY *TempMenuEntry; UINTN i, j, VolumeIndex; UINT64 osind; CHAR8 *b = 0; UINT32 CsrValue; + LOG(1, LOG_LINE_SEPARATOR, L"Scanning for tools"); MokLocations = StrDuplicate(MOK_LOCATIONS); if (MokLocations != NULL) MergeStrings(&MokLocations, SelfDirPath, L','); - HiddenTools = ReadHiddenTags(L"HiddenTools"); - if ((HiddenTools) && (StrLen(HiddenTools) > 0)) { - MergeStrings(&GlobalConfig.DontScanTools, HiddenTools, L','); - } - MyFreePool(HiddenTools); - for (i = 0; i < NUM_TOOLS; i++) { switch(GlobalConfig.ShowTools[i]) { // NOTE: Be sure that FileName is NULL at the end of each case. case TAG_SHUTDOWN: TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN); + LOG(2, LOG_LINE_NORMAL, L"Adding Shutdown tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; case TAG_REBOOT: TempMenuEntry = CopyMenuEntry(&MenuEntryReset); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET); + LOG(2, LOG_LINE_NORMAL, L"Adding Reboot tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; case TAG_ABOUT: TempMenuEntry = CopyMenuEntry(&MenuEntryAbout); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); + LOG(2, LOG_LINE_NORMAL, L"Adding Info/About tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; case TAG_EXIT: TempMenuEntry = CopyMenuEntry(&MenuEntryExit); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT); + LOG(2, LOG_LINE_NORMAL, L"Adding Exit tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; @@ -1329,6 +1523,7 @@ if (GlobalConfig.HiddenTags) { TempMenuEntry = CopyMenuEntry(&MenuEntryManageHidden); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_HIDDEN); + LOG(2, LOG_LINE_NORMAL, L"Adding Hidden tag"); AddMenuEntry(&MainMenu, TempMenuEntry); } break; @@ -1339,28 +1534,41 @@ if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) { TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE); + LOG(2, LOG_LINE_NORMAL, L"Adding Reboot-to-Firmware tag"); AddMenuEntry(&MainMenu, TempMenuEntry); - } // if + } else { + LOG(1, LOG_LINE_NORMAL, L"showtools includes firmware, but EFI lacks support"); + } // if/else MyFreePool(b); - } // if + } else { + LOG(1, LOG_LINE_NORMAL, L"showtools includes firmware, but OsIndicationsSupported is missing"); + } break; case TAG_SHELL: j = 0; while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) { if (IsValidTool(SelfVolume, FileName)) { - AddToolEntry(SelfVolume, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), + LOG(1, LOG_LINE_NORMAL, L"Adding Shell tag for '%s' on '%s'", FileName, + SelfVolume->VolName); + AddToolEntry(SelfVolume, FileName, L"EFI Shell", + BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE); } - MyFreePool(FileName); + MyFreePool(FileName); + FileName = NULL; } // while + ScanFirmwareDefined(1, L"Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL)); break; case TAG_GPTSYNC: j = 0; while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) { if (IsValidTool(SelfVolume, FileName)) { - AddToolEntry(SelfVolume, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART), + LOG(1, LOG_LINE_NORMAL, L"Adding Hybrid MBR tool tag for '%s' on '%s'", FileName, + SelfVolume->VolName); + AddToolEntry(SelfVolume, FileName, L"Hybrid MBR tool", + BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE); } // if MyFreePool(FileName); @@ -1372,6 +1580,8 @@ j = 0; while ((FileName = FindCommaDelimited(GDISK_NAMES, j++)) != NULL) { if (IsValidTool(SelfVolume, FileName)) { + LOG(1, LOG_LINE_NORMAL, L"Adding GPT fdisk tag for '%s' on '%s'", FileName, + SelfVolume->VolName); AddToolEntry(SelfVolume, FileName, L"disk partitioning tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'G', FALSE); } // if @@ -1384,6 +1594,8 @@ j = 0; while ((FileName = FindCommaDelimited(NETBOOT_NAMES, j++)) != NULL) { if (IsValidTool(SelfVolume, FileName)) { + LOG(1, LOG_LINE_NORMAL, L"Adding Netboot tag for '%s' on '%s'", FileName, + SelfVolume->VolName); AddToolEntry(SelfVolume, FileName, L"Netboot", BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT), 'N', FALSE); } // if @@ -1398,9 +1610,12 @@ while ((FileName = FindCommaDelimited(GlobalConfig.MacOSRecoveryFiles, j++)) != NULL) { if ((Volumes[VolumeIndex]->RootDir != NULL)) { if ((IsValidTool(Volumes[VolumeIndex], FileName))) { - SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName); + Description = PoolPrint(L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName); + LOG(1, LOG_LINE_NORMAL, L"Adding Apple Recovery tag for '%s' on '%s'", FileName, + Volumes[VolumeIndex]->VolName); AddToolEntry(Volumes[VolumeIndex], FileName, Description, BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE); + MyFreePool(Description); } // if } // if } // while @@ -1415,16 +1630,20 @@ if ((Volumes[VolumeIndex]->RootDir != NULL) && (IsValidTool(Volumes[VolumeIndex], FileName)) && ((VolName == NULL) || MyStriCmp(VolName, Volumes[VolumeIndex]->VolName))) { - SPrint(Description, 255, L"Microsoft Recovery on %s", Volumes[VolumeIndex]->VolName); + Description = PoolPrint(L"Microsoft Recovery on %s", Volumes[VolumeIndex]->VolName); + LOG(1, LOG_LINE_NORMAL, + L"Adding Windows Recovery tag for '%s' on '%s'", + FileName, Volumes[VolumeIndex]->VolName); AddToolEntry(Volumes[VolumeIndex], FileName, Description, BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE), 'R', TRUE); + MyFreePool(Description); } // if } // for + MyFreePool(FileName); + MyFreePool(VolName); + VolName = NULL; } // while - MyFreePool(FileName); FileName = NULL; - MyFreePool(VolName); - VolName = NULL; break; case TAG_MOK_TOOL: @@ -1439,6 +1658,7 @@ if ((GetCsrStatus(&CsrValue) == EFI_SUCCESS) && (GlobalConfig.CsrValues)) { TempMenuEntry = CopyMenuEntry(&MenuEntryRotateCsr); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_CSR_ROTATE); + LOG(1, LOG_LINE_NORMAL, L"Adding CSR Rotate tag"); AddMenuEntry(&MainMenu, TempMenuEntry); } // if break; @@ -1446,12 +1666,14 @@ case TAG_INSTALL: TempMenuEntry = CopyMenuEntry(&MenuEntryInstall); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_INSTALL); + LOG(1, LOG_LINE_NORMAL, L"Adding Install tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; case TAG_BOOTORDER: TempMenuEntry = CopyMenuEntry(&MenuEntryBootorder); TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_BOOTORDER); + LOG(1, LOG_LINE_NORMAL, L"Adding Boot Order tag"); AddMenuEntry(&MainMenu, TempMenuEntry); break; @@ -1461,4 +1683,5 @@ } // switch() } // for + LOG(2, LOG_LINE_NORMAL, L"Done scanning for tools"); } // VOID ScanForTools diff -Nru refind-0.12.0/refind/screen.c refind-0.13.2/refind/screen.c --- refind-0.12.0/refind/screen.c 2020-03-07 20:19:47.000000000 +0000 +++ refind-0.13.2/refind/screen.c 2021-02-27 04:01:39.000000000 +0000 @@ -62,6 +62,7 @@ #include "lib.h" #include "menu.h" #include "mystrings.h" +#include "log.h" #include "../include/refit_call_wrapper.h" #include "../include/egemb_refind_banner.h" @@ -87,8 +88,6 @@ // general defines and variables -static BOOLEAN haveError = FALSE; - static VOID PrepareBlankLine(VOID) { UINTN i; @@ -106,13 +105,16 @@ VOID InitScreen(VOID) { + LOG(1, LOG_LINE_NORMAL, L"Entering InitScreen()"); // initialize libeg egInitScreen(); if (egHasGraphicsMode()) { + LOG(2, LOG_LINE_NORMAL, L"Have graphics mode; setting screen size"); egGetScreenSize(&UGAWidth, &UGAHeight); AllowGraphicsMode = TRUE; } else { + LOG(2, LOG_LINE_NORMAL, L"No graphics mode detected; setting text mode"); AllowGraphicsMode = FALSE; egSetTextMode(GlobalConfig.RequestedTextMode); egSetGraphicsModeEnabled(FALSE); // just to be sure we are in text mode @@ -123,7 +125,9 @@ refit_call2_wrapper(ST->ConOut->EnableCursor, ST->ConOut, FALSE); // get size of text console - if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, ST->ConOut->Mode->Mode, &ConWidth, &ConHeight) != EFI_SUCCESS) { + if (refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, + ST->ConOut->Mode->Mode, &ConWidth, + &ConHeight) != EFI_SUCCESS) { // use default values on error ConWidth = 80; ConHeight = 25; @@ -141,6 +145,7 @@ { UINTN NewWidth, NewHeight; + LOG(1, LOG_LINE_NORMAL, L"Setting screen resolution and mode"); // Convert mode number to horizontal & vertical resolution values if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight == 0)) egGetResFromMode(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight)); @@ -151,6 +156,7 @@ if ((GlobalConfig.RequestedScreenWidth > 0) && (GlobalConfig.RequestedScreenHeight > 0)) { UGAWidth = (UGAWidth < GlobalConfig.RequestedScreenWidth) ? UGAWidth : GlobalConfig.RequestedScreenWidth; UGAHeight = (UGAHeight < GlobalConfig.RequestedScreenHeight) ? UGAHeight : GlobalConfig.RequestedScreenHeight; + LOG(2, LOG_LINE_NORMAL, L"Recording current resolution as %dx%d", UGAWidth, UGAHeight); } // Set text mode. If this requires increasing the size of the graphics mode, do so. @@ -159,8 +165,12 @@ if ((NewWidth > UGAWidth) || (NewHeight > UGAHeight)) { UGAWidth = NewWidth; UGAHeight = NewHeight; + LOG(2, LOG_LINE_NORMAL, L"After setting text mode, recording new current resolution as %dx%d", + UGAWidth, UGAHeight); } - if ((UGAWidth > GlobalConfig.RequestedScreenWidth) || (UGAHeight > GlobalConfig.RequestedScreenHeight)) { + if ((UGAWidth > GlobalConfig.RequestedScreenWidth) || + (UGAHeight > GlobalConfig.RequestedScreenHeight)) { + LOG(2, LOG_LINE_NORMAL, L"Adjusting requested screen size based on actual screen size"); // Requested text mode forces us to use a bigger graphics mode GlobalConfig.RequestedScreenWidth = UGAWidth; GlobalConfig.RequestedScreenHeight = UGAHeight; @@ -168,11 +178,14 @@ } if (GlobalConfig.RequestedScreenWidth > 0) { + LOG(2, LOG_LINE_NORMAL, L"Setting screen size to %dx%d", GlobalConfig.RequestedScreenWidth, + GlobalConfig.RequestedScreenHeight); egSetScreenSize(&(GlobalConfig.RequestedScreenWidth), &(GlobalConfig.RequestedScreenHeight)); egGetScreenSize(&UGAWidth, &UGAHeight); } // if user requested a particular screen resolution if (GlobalConfig.TextOnly) { + LOG(2, LOG_LINE_NORMAL, L"Setting text-only mode"); // switch to text mode if requested AllowGraphicsMode = FALSE; SwitchToText(FALSE); @@ -180,6 +193,7 @@ // clear screen and show banner // (now we know we'll stay in graphics mode) if ((UGAWidth >= HIDPI_MIN) && !HaveResized) { + LOG(2, LOG_LINE_NORMAL, L"Doubling icon sizes for HiDPI display"); GlobalConfig.IconSizes[ICON_SIZE_BADGE] *= 2; GlobalConfig.IconSizes[ICON_SIZE_SMALL] *= 2; GlobalConfig.IconSizes[ICON_SIZE_BIG] *= 2; @@ -224,20 +238,6 @@ { DrawScreenHeader(Title); SwitchToText(FALSE); - - // reset error flag - haveError = FALSE; -} - -VOID FinishTextScreen(IN BOOLEAN WaitAlways) -{ - if (haveError || WaitAlways) { - PauseForKey(); - SwitchToText(FALSE); - } - - // reset error flag - haveError = FALSE; } VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title) @@ -253,9 +253,6 @@ DrawScreenHeader(Title); SwitchToText(TRUE); } - - // reset error flag - haveError = FALSE; } VOID FinishExternalScreen(VOID) @@ -263,16 +260,8 @@ // make sure we clean up later GraphicsScreenDirty = TRUE; - if (haveError) { - SwitchToText(FALSE); - PauseForKey(); - } - // Reset the screen resolution, in case external program changed it.... SetupScreen(); - - // reset error flag - haveError = FALSE; } VOID TerminateScreen(VOID) @@ -332,7 +321,7 @@ return GotKeyStrokes; } -// Displays *text without regard to appearances. Used mainly for debugging +// Displays *Text without regard to appearances. Used mainly for debugging // and rare error messages. // Position code is used only in graphics mode. // TODO: Improve to handle multi-line text. @@ -371,18 +360,6 @@ refit_call1_wrapper(BS->Stall, 1000000 * Seconds); } // VOID PauseSeconds() -#if REFIT_DEBUG > 0 -VOID DebugPause(VOID) -{ - // show console and wait for key - SwitchToText(FALSE); - PauseForKey(); - - // reset error flag - haveError = FALSE; -} -#endif - VOID EndlessIdleLoop(VOID) { UINTN index; @@ -414,7 +391,7 @@ refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR); PrintUglyText(Temp, NEXTLINE); refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC); - haveError = TRUE; + LOG(1, LOG_LINE_NORMAL, Temp); MyFreePool(Temp); return TRUE; @@ -437,7 +414,7 @@ refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR); PrintUglyText(Temp, NEXTLINE); refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC); - haveError = TRUE; + LOG(1, LOG_LINE_NORMAL, Temp); MyFreePool(Temp); return TRUE; @@ -500,7 +477,6 @@ GlobalConfig.BannerBottomEdge = BannerPosY + Banner->Height; if (GlobalConfig.ScreensaverTime != -1) BltImage(Banner, (UINTN) BannerPosX, (UINTN) BannerPosY); - egFreeImage(Banner); } } else { // not showing banner diff -Nru refind-0.12.0/refind/screen.h refind-0.13.2/refind/screen.h --- refind-0.12.0/refind/screen.h 2017-07-31 16:13:54.000000000 +0000 +++ refind-0.13.2/refind/screen.h 2021-02-27 04:01:39.000000000 +0000 @@ -86,7 +86,6 @@ VOID InitScreen(VOID); VOID SetupScreen(VOID); VOID BeginTextScreen(IN CHAR16 *Title); -VOID FinishTextScreen(IN BOOLEAN WaitAlways); VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title); VOID FinishExternalScreen(VOID); VOID TerminateScreen(VOID); Binary files /tmp/tmphkhbxscj/7yBgQRGC6X/refind-0.12.0/refind/tools_x64/gptsync_x64.efi and /tmp/tmphkhbxscj/XuqBXwZh3b/refind-0.13.2/refind/tools_x64/gptsync_x64.efi differ diff -Nru refind-0.12.0/refind.conf-sample refind-0.13.2/refind.conf-sample --- refind-0.12.0/refind.conf-sample 2020-03-02 13:01:10.000000000 +0000 +++ refind-0.13.2/refind.conf-sample 2021-03-08 04:03:53.000000000 +0000 @@ -12,6 +12,16 @@ # timeout 20 +# Set the logging level. When set to 0, rEFInd does not log its actions. +# When set to 1 or above, rEFInd creates a file called refind.log in +# its home directory on the ESP and records information about what it's +# doing. Higher values record more information, up to a maximum of 4. +# This token should be left at the default of 0 except when debugging +# problems. +# Default value is 0 +# +#log_level 1 + # Normally, when the timeout period has passed, rEFInd boots the # default_selection. If the following option is uncommented, though, # rEFInd will instead attempt to shut down the computer. @@ -36,7 +46,7 @@ # variables. # Default is true # -#use_nvram false +use_nvram false # Screen saver timeout; the screen blanks after the specified number of # seconds with no keyboard input. The screen returns after most keypresses @@ -160,9 +170,10 @@ # #textmode 2 -# Set the screen's video resolution. Pass this option either: -# * two values, corresponding to the X and Y resolutions -# * one value, corresponding to a GOP (UEFI) video mode +# Set the screen's video resolution. Pass this option one of the following: +# * two integer values, corresponding to the X and Y resolutions +# * one integer value, corresponding to a GOP (UEFI) video mode +# * the string "max", which sets the maximum available resolution # Note that not all resolutions are supported. On UEFI systems, passing # an incorrect value results in a message being shown on the screen to # that effect, along with a list of supported modes. On EFI 1.x systems @@ -175,6 +186,7 @@ #resolution 1024 768 #resolution 1440 900 #resolution 3 +#resolution max # Enable touch screen support. If active, this feature enables use of # touch screen controls (as on tablets). Note, however, that not all @@ -201,7 +213,7 @@ # Size of the mouse pointer, in pixels, per side. # Default is 16 # -#mouse_size +#mouse_size 16 # Speed of mouse tracking. Higher numbers equate to faster # mouse movement. This option requires that enable_mouse be @@ -300,6 +312,7 @@ # biosexternal - BIOS external boot loaders (USB, eSATA, etc.) # cd - BIOS optical-disc boot loaders # manual - use stanzas later in this configuration file +# firmware - boot EFI programs set in the firmware's NVRAM # 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 @@ -307,7 +320,7 @@ # On UEFI PCs, default is internal,external,optical,manual # On Macs, default is internal,hdbios,external,biosexternal,optical,cd,manual # -#scanfor internal,external,optical,manual +#scanfor internal,external,optical,manual,firmware # By default, rEFInd relies on the UEFI firmware to detect BIOS-mode boot # devices. This sometimes doesn't detect all the available devices, though. @@ -399,6 +412,17 @@ # #dont_scan_files shim.efi,MokManager.efi +# EFI NVRAM Boot#### variables that should NOT be presented as loaders +# when "firmware" is an option to "scanfor". The comma-separated list +# presented here contains strings that are matched against the +# description field -- if a value here is a case-insensitive substring +# of the boot option description, then it will be excluded from the +# boot list. To specify a string that includes a space, enclose it +# in quotes. Specifying "shell" will counteract the automatic +# inclusion of built-in EFI shells. +# +#dont_scan_firmware HARDDISK,shell,"Removable Device" + # Scan for Linux kernels that lack a ".efi" filename extension. This is # useful for better integration with Linux distributions that provide # kernels with EFI stub loaders but that don't give those kernels filenames @@ -440,6 +464,14 @@ # #extra_kernel_version_strings linux-lts,linux +# Write to systemd EFI variables (currently only LoaderDevicePartUUID) when +# launching Linux via an EFI stub loader, ELILO, or GRUB. This variable, +# when present, causes systemd to mount the ESP at /boot or /efi *IF* either +# directory is empty and nothing else is mounted there. +# Default is "false" +# +#write_systemd_vars true + # Set the maximum number of tags that can be displayed on the screen at # any time. If more loaders are discovered than this value, rEFInd shows # a subset in a scrolling list. If this value is set too high for the @@ -453,7 +485,7 @@ # keyboard accelerators available within rEFInd. You may select the # default loader using: # - A digit between 1 and 9, in which case the Nth loader in the menu -# will be the default. +# will be the default. # - A "+" symbol at the start of the string, which refers to the most # recently booted loader. # - Any substring that corresponds to a portion of the loader's title @@ -506,20 +538,25 @@ #spoof_osx_version 10.9 # Set the CSR values for Apple's System Integrity Protection (SIP) feature. -# Values are one-byte (two-character) hexadecimal numbers. These values +# Values are two-byte (four-character) hexadecimal numbers. These values # define which specific security features are enabled. Below are the codes # for what the values mean. Add them up (in hexadecimal!) to set new values. # Apple's "csrutil enable" and "csrutil disable" commands set values of 10 -# and 77, respectively. -# CSR_ALLOW_UNTRUSTED_KEXTS 0x01 -# CSR_ALLOW_UNRESTRICTED_FS 0x02 -# CSR_ALLOW_TASK_FOR_PID 0x04 -# CSR_ALLOW_KERNEL_DEBUGGER 0x08 -# CSR_ALLOW_APPLE_INTERNAL 0x10 -# CSR_ALLOW_UNRESTRICTED_DTRACE 0x20 -# CSR_ALLOW_UNRESTRICTED_NVRAM 0x40 -# -#csr_values 10,77 +# and 877, respectively. (Prior to OS 11, 77 was used rather than 877; 877 +# is required for OS 11, and should work for OS X 10.x, too.) +# CSR_ALLOW_UNTRUSTED_KEXTS 0x0001 +# CSR_ALLOW_UNRESTRICTED_FS 0x0002 +# CSR_ALLOW_TASK_FOR_PID 0x0004 +# CSR_ALLOW_KERNEL_DEBUGGER 0x0008 +# CSR_ALLOW_APPLE_INTERNAL 0x0010 +# CSR_ALLOW_UNRESTRICTED_DTRACE 0x0020 +# CSR_ALLOW_UNRESTRICTED_NVRAM 0x0040 +# CSR_ALLOW_DEVICE_CONFIGURATION 0x0080 +# CSR_ALLOW_ANY_RECOVERY_OS 0x0100 +# CSR_ALLOW_UNAPPROVED_KEXTS 0x0200 +# CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE 0x0400 +# CSR_ALLOW_UNAUTHENTICATED_ROOT 0x0800 +#csr_values 10,877 # Include a secondary configuration file within this one. This secondary # file is loaded as if its options appeared at the point of the "include" @@ -641,7 +678,7 @@ disabled } -# Mac OS is normally detected and run automatically; however, +# MacOS is normally detected and run automatically; however, # if you want to do something unusual, a manual boot stanza may # be the way to do it. This one does nothing very unusual, but # it may serve as a starting point. Note that you'll almost @@ -653,3 +690,19 @@ loader \System\Library\CoreServices\boot.efi disabled } + +# The firmware_bootnum token takes a HEXADECIMAL value as an option +# and sets that value using the EFI's BootNext variable and then +# reboots the computer. This then causes a one-time boot of the +# computer using this EFI boot option. It can be used for various +# purposes, but one that's likely to interest some rEFInd users is +# that some Macs with HiDPI displays produce lower-resolution +# desktops when booted through rEFInd than when booted via Apple's +# own boot manager. Booting using the firmware_bootnum option +# produces the better resolution. Note that no loader option is +# used in this type of configuration. +menuentry "macOS via BootNext" { + icon /EFI/refind/icons/os_mac.png + firmware_bootnum 80 + disabled +} diff -Nru refind-0.12.0/refind.inf refind-0.13.2/refind.inf --- refind-0.12.0/refind.inf 2020-03-01 18:27:53.000000000 +0000 +++ refind-0.13.2/refind.inf 2021-02-27 04:01:39.000000000 +0000 @@ -49,6 +49,7 @@ refind/lib.c refind/line_edit.c refind/linux.c + refind/log.c refind/main.c refind/menu.c refind/mystrings.c diff -Nru refind-0.12.0/refind-install refind-0.13.2/refind-install --- refind-0.12.0/refind-install 2020-03-13 12:43:32.000000000 +0000 +++ refind-0.13.2/refind-install 2021-03-13 23:59:45.000000000 +0000 @@ -38,6 +38,8 @@ # Revision history: # +# 0.13.2 -- Add installation of rEFInd Secure Boot key as MOK; if installing +# with Shim, add backup direct boot NVRAM option # 0.12.0 -- Improve code to find ESPs; should help with NVMe drives. Also # activate install and bootorder features when using --usedefault # option. @@ -267,11 +269,18 @@ case "$OSTYPE" in darwin*) CpuType=`ioreg -l -p IODeviceTree | grep firmware-abi | cut -d "\"" -f 4` - if [[ "$CpuType" == EFI32 ]] ; then - Platform="ia32" - else + case "$CpuType" in + EFI64) Platform="x64" - fi + ;; + EFI32) + Platform="ia32" + ;; + *) + echo "Unknown CPU type '$CpuType'; aborting!" + exit 1 + ;; + esac ;; linux*) CpuType=`uname -m` @@ -787,7 +796,7 @@ # Create a BOOT.CSV file in the same directory as rEFInd, to help in recovery # should the system's boot entry list be lost CreateBootCsvFile() { - IConv=`which iconv 2> /dev/null` + IConv="$(command -v iconv 2> /dev/null)" if [[ -x "$IConv" ]] ; then if [[ "$Platform" == "x64" && -d "$InstallDir/$TargetDir" ]] ; then echo "$TargetX64,rEFInd Boot Manager,,This is the boot entry for rEFInd" | \ @@ -879,7 +888,7 @@ <key>ProductName</key> <string>rEFInd</string> <key>ProductVersion</key> - <string>0.12.0</string> + <string>0.13.2</string> </dict> </plist> ENDOFHERE @@ -1052,7 +1061,7 @@ PrivateKey="$EtcKeysDir/$LocalKeysBase.key" CertKey="$EtcKeysDir/$LocalKeysBase.crt" DerKey="$EtcKeysDir/$LocalKeysBase.cer" - OpenSSL=`which openssl 2> /dev/null` + OpenSSL="$(command -v openssl 2> /dev/null)" # Do the work only if one or more of the necessary keys is missing # TODO: Technically, we don't need the DerKey; but if it's missing and openssl @@ -1122,7 +1131,7 @@ # key files in $EtcKeysDir. If they're present, use them to re-sign the binaries. If # not, try to generate new keys and store them in $EtcKeysDir. ReSignBinaries() { - SBSign=`which sbsign 2> /dev/null` + SBSign="$(command -v sbsign 2> /dev/null)" echo "Found sbsign at $SBSign" TempDir="/tmp/refind_local" if [[ ! -x "$SBSign" ]] ; then @@ -1166,8 +1175,8 @@ local TypeCode echo "The ESP doesn't seem to be mounted! Trying to find it...." - Dmraid=`which dmraid 2> /dev/null` - Sgdisk=`which sgdisk 2> /dev/null` + Dmraid="$(command -v dmraid 2> /dev/null)" + Sgdisk="$(command -v sgdisk 2> /dev/null)" if [[ ! -x "$Sgdisk" ]] ; then echo "The sgdisk program (part of the gdisk package) is not installed; please" echo "install it and try again." @@ -1267,8 +1276,9 @@ local KeepExistingEntry=0 local ExistingEntryBootNum local FirstBoot + local RealRefindBinary - Efibootmgr=`which efibootmgr 2> /dev/null` + Efibootmgr="$(command -v efibootmgr 2> /dev/null)" if [[ "$Efibootmgr" ]] ; then InstallPart=`grep "$InstallDir " /etc/mtab | grep -v autofs | cut -d " " -f 1 | tail -n1` for Name in `lsblk -r | grep disk | cut -f 1 -d " "` ; do @@ -1307,11 +1317,26 @@ if [[ "$KeepExistingEntry" == 0 ]] ; then echo "Creating new NVRAM entry" + # If this is a Shim-based installation, then also create a direct + # entry to rEFInd's own binary as a fallback. This is helpful in + # case of package scripts that tend to pass --shim even when the + # system is configured with custom SB variables, making use of Shim + # inappropriate. + if [[ $Refind == *"shim"* ]]; then + if [[ "$KeepName" == 1 ]] ; then + RealRefindBinary="$TargetDir/refind_$Platform.efi" + else + RealRefindBinary="$TargetDir/grub$Platform.efi" + fi + "$Efibootmgr" -c -l "$RealRefindBinary" -L "rEFInd Boot Manager (direct)" \ + -d $InstallDisk -p $PartNum &> /dev/null + fi if [[ "$KeepName" == 0 ]] ; then - "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null + "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk \ + -p $PartNum &> /dev/null else - "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum \ - -u "$TargetShim $TargetX64" &> /dev/null + "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk \ + -p $PartNum -u "$TargetShim $TargetX64" &> /dev/null fi else echo "Keeping existing NVRAM entry" @@ -1374,6 +1399,45 @@ fi } +# Install a Secure Boot key as a MOK +InstallSBKey() { + local UsedDerKey + if [ "$LocalKeys" == "1" ] ; then + UsedDerKey="$DerKey" + else + UsedDerKey="$ThisDir/keys/refind.cer" + fi + if command -v mokutil &>/dev/null ; then + if mokutil --test-key "$UsedDerKey" &> /dev/null ; then + echo "rEFInd requires a Secure Boot key to be used with Secure Boot active. This" + echo "script can install the key as a Machine Owner Key (MOK) for use with Shim." + echo "If you've taken full control of Secure Boot on your computer and are NOT" + echo "using Shim, you will instead need to add the key with efi-updatevar in" + echo "Linux, KeyTool in the UEFI, or the firmware's built-in key maintenance" + echo "tools." + echo "" + echo -n "Do you want to add the key as a MOK for use with Shim? (Y/N) " + ReadYesNo + echo "" + if [[ "$YesNo" == "Y" || "$YesNo" == "y" ]] ; then + echo "You will now be asked to enter a password twice. When you reboot, a blue" + echo "screen should appear asking if you want to press a key to manage MOKs. Do" + echo "so, and then select the option to 'Enroll MOK'. After you confirm the" + echo "enrollment, you will be asked to enter the password you're about to type." + mokutil -i "$UsedDerKey" + if [[ $? != 0 ]] ; then + echo "Problem detected installing MOK!" + Problems=1 + fi + fi + echo "For more on rEFInd and Secure Boot, see:" + echo "https://www.rodsbooks.com/refind/secureboot.html" + else + echo "The appropriate Secure Boot key is already enrolled." + fi + fi +} # InstallSBKey() + # Controls rEFInd installation under Linux. # Sets Problems=1 if something goes wrong. InstallOnLinux() { @@ -1400,6 +1464,14 @@ AddBootEntry GenerateRefindLinuxConf fi + # Note that InstallSBKey may require the user to enter a password, so + # it must NOT be called if --yes is passed to the script, since that + # parameter is intended to eliminate user interaction. The user will + # just have to enroll any necessary Secure Boot key manually in this + # case. + if [[ "$IsSecureBoot" == "1" && "$AlwaysYes" == "0" ]] ; then + InstallSBKey + fi } # InstallOnLinux() # diff -Nru refind-0.12.0/refind-mkdefault refind-0.13.2/refind-mkdefault --- refind-0.12.0/refind-mkdefault 2016-04-23 23:07:25.000000000 +0000 +++ refind-0.13.2/refind-mkdefault 2021-03-07 18:29:24.000000000 +0000 @@ -200,5 +200,6 @@ print("No changes saved.") return(retval) + if __name__ == '__main__': sys.exit(main()) diff -Nru refind-0.12.0/refind.spec refind-0.13.2/refind.spec --- refind-0.12.0/refind.spec 2020-03-13 12:43:02.000000000 +0000 +++ refind-0.13.2/refind.spec 2021-03-13 23:59:07.000000000 +0000 @@ -1,13 +1,13 @@ Summary: EFI boot manager software Name: refind -Version: 0.12.0 +Version: 0.13.2 Release: 1%{?dist} Summary: EFI boot manager software License: GPLv3 URL: http://www.rodsbooks.com/refind/ Group: System Environment/Base Source: refind-src-%version.tar.gz -Requires: efibootmgr gdisk +Requires: efibootmgr gdisk mokutil BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %define efiarch unknown @@ -176,6 +176,12 @@ # thus wiping out the just-updated files. %changelog +* Sat Mar 13 2021 R Smith <rodsmith@rodsbooks.com> - 0.13.2 +- Updated spec file for 0.13.2 +* Wed Feb 24 2021 R Smith <rodsmith@rodsbooks.com> - 0.13.1 +- Updated spec file for 0.13.1 +* Mon Feb 15 2021 R Smith <rodsmith@rodsbooks.com> - 0.13.0 +- Updated spec file for 0.13.0 * Fri Mar 13 2020 R Smith <rodsmith@rodsbooks.com> - 0.12.0 - Updated spec file for 0.12.0 * Wed Feb 12 2020 R Smith <rodsmith@rodsbooks.com> - 0.11.5