pax_global_header00006660000000000000000000000064133073030320014504gustar00rootroot0000000000000052 comment=e067160ecef8208e1944002e5d50b275733211fb efibootmgr-17/000077500000000000000000000000001330730303200134325ustar00rootroot00000000000000efibootmgr-17/.gitignore000066400000000000000000000001601330730303200154170ustar00rootroot00000000000000.*.sw? *.E *.o *.patch *.S efibootmgr-*.tar.* core.* efibootmgr*.zip efibootmgr.spec .*.d cov-int scan-results/ efibootmgr-17/.travis.yml000066400000000000000000000013271330730303200155460ustar00rootroot00000000000000language: c cache: ccache branches: except: - travis matrix: include: - os: linux dist: trusty services: docker before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull vathpela/efi-ci-rawhide:v0 ; fi script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run vathpela/efi-ci-rawhide:v0 /bin/sh -c "cd /root/ && ./build.sh --branch \"$TRAVIS_BRANCH\" --commit \"$TRAVIS_COMMIT\" --commit-range \"$TRAVIS_COMMIT_RANGE\" --event-type \"$TRAVIS_EVENT_TYPE\" --pull-request \"$TRAVIS_PULL_REQUEST\" --pr-branch \"$TRAVIS_PULL_REQUEST_BRANCH\" --pr-sha \"$TRAVIS_PULL_REQUEST_SHA\" --remote \"$TRAVIS_PULL_REQUEST_SLUG\" --repo \"$TRAVIS_REPO_SLUG\" --test-subject efibootmgr" ; fi efibootmgr-17/AUTHORS000066400000000000000000000014331330730303200145030ustar00rootroot00000000000000Matt Domsch - All .c and .h files Andreas Schwab - Patches to several .c and .h files Richard Hirst - Patch to efichar.c dann frazier - docbook of manpage - Patches to efibootmgr.c - network boot entry creation in efi.c Joshua Giles - walk the PCI path inserting parent bridge device path components for network boot and EDD30 entries. Alex Williamson - Patch to efi.c and efibootmgr.c for handling BootXXXX values using uppercase hex rather than lowercase, per EFI 1.10 spec. Rogerio Timmers - add option -@ for passing extra variable options in from a file, necessary for setting up some boot entries for Microsoft Windows. efibootmgr-17/COPYING000066400000000000000000000431271330730303200144740ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. efibootmgr-17/INSTALL000066400000000000000000000007321330730303200144650ustar00rootroot00000000000000Running 'make' builds the file src/efibootmgr. efibootmgr should be placed into /usr/sbin/. efibootmgr currently requires efivar version 0.19, as well as the pkg-config utility, to be built. If you receive a message like the following: src/include/efi.h:32:20: fatal error: efivar.h: No such file or directory then you have not installed these dependencies, which can be found at: efivar: https://github.com/rhboot/efivar pkg-config: https://pkgconfig.freedesktop.org efibootmgr-17/Make.coverity000066400000000000000000000024551330730303200161030ustar00rootroot00000000000000COV_EMAIL=$(call get-config,coverity.email) COV_TOKEN=$(call get-config,coverity.token) COV_URL=$(call get-config,coverity.url) COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 cov-int : clean cov-build --dir cov-int make all cov-clean : @rm -vf $(NAME)-coverity-*.tar.* @if [[ -d cov-int ]]; then rm -rf cov-int && echo "removed 'cov-int'"; fi cov-file : | $(COV_FILE) $(COV_FILE) : cov-int tar caf $@ cov-int cov-upload : @if [[ -n "$(COV_URL)" ]] && \ [[ -n "$(COV_TOKEN)" ]] && \ [[ -n "$(COV_EMAIL)" ]] ; \ then \ echo curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ else \ echo Coverity output is in $(COV_FILE) ; \ fi coverity : | cov-test coverity : cov-file cov-upload clean : | cov-clean COV_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions cov-build 2>/dev/null) ; [ -n "$$x" ] && echo 1) ifeq ($(COV_BUILD),) COV_BUILD_ERROR = $(error cov-build not found) endif cov-test : ; $(COV_BUILD_ERROR) .PHONY : coverity cov-upload cov-clean cov-file cov-test efibootmgr-17/Make.defaults000066400000000000000000000043001330730303200160350ustar00rootroot00000000000000NAME = efibootmgr prefix ?= /usr libdir ?= $(prefix)/lib64 datadir ?= $(prefix)/share mandir ?= $(datadir)/man includedir ?= $(prefix)/include bindir ?= $(prefix)/bin sbindir ?= $(prefix)/sbin localedir ?= $(datadir)/locale/ PCDIR ?= $(libdir)/pkgconfig DESTDIR ?= ifneq ($(origin EXTRALIBDIRS),undefined) override EXTRALIBDIR := $(foreach dir,$(EXTRALIBDIRS),-L$(dir)) endif ifneq ($(origin EXTRAINCDIRS),undefined) override EXTRAINCDIR := $(foreach dir,$(EXTRAINCDIRS),-I$(dir)) endif EFIDIR ?= $(shell x=$$(which --skip-alias --skip-functions git 2>/dev/null) ; [ -n "$$x" ] && git config --get efibootmgr.efidir) ifeq ($(EFIDIR),) EFIDIR_ERROR = $(error EFIDIR or .gitconfig efibootmgr.efidir must be set to this distro's reserved EFI System Partition subdirectory name) endif EFI_LOADER := grub.efi INSTALL ?= install CROSS_COMPILE ?= PKG_CONFIG = $(CROSS_COMPILE)pkg-config CC := $(if $(filter default,$(origin CC)),$(CROSS_COMPILE)gcc,$(CC)) CCLD := $(if $(filter undefined,$(origin CCLD)),$(CC),$(CCLD)) CFLAGS ?= -O2 -g -flto AR := $(CROSS_COMPILE)gcc-ar NM := $(CROSS_COMPILE)gcc-nm RANLIB := $(CROSS_COMPILE)gcc-ranlib PKGS = SUBDIR_CFLAGS ?= clang_cflags = gcc_cflags = cflags = $(EXTRALIBDIR) $(EXTRAINCDIR) $(CFLAGS) $(SUBDIR_CFLAGS) \ -Werror -Wall -Wextra -Wsign-compare -Wstrict-aliasing \ -std=gnu11 -fPIC \ -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -DLOCALEDIR=\"$(localedir)\" \ -DEFIBOOTMGR_VERSION="\"$(VERSION)\"" \ -DDEFAULT_LOADER=\"\\\\EFI\\\\$(EFIDIR)\\\\$(EFI_LOADER)\" \ $(if $(findstring clang,$(CC)),$(clang_cflags),) \ $(if $(findstring gcc,$(CC)),$(gcc_cflags),) \ $(call pkg-config-cflags) clang_ccldflags = gcc_ccldflags = -fno-merge-constants \ -Wl,--fatal-warnings,--no-allow-shlib-undefined \ -Wl,-O2 -Wl,--no-undefined-version ccldflags = $(cflags) $(CCLDFLAGS) $(LDFLAGS) \ $(if $(findstring clang,$(CCLD)),$(clang_ccldflags),) \ $(if $(findstring gcc,$(CCLD)),$(gcc_ccldflags),) \ $(call pkg-config-ldflags) CPPFLAGS?= SOFLAGS=-shared LDLIBS=$(foreach lib,$(LIBS),-l$(lib)) $(call pkg-config-ldlibs) .PHONY: check_efidir_error check_efidir_error : ; $(EFIDIR_ERROR) $(info Building with EFIDIR as $(EFIDIR)) COMMIT_ID=$(shell git log -1 --pretty=%H 2>/dev/null || echo master) efibootmgr-17/Make.deps000066400000000000000000000005021330730303200151610ustar00rootroot00000000000000SRCDIR = $(realpath .) TOPDIR = $(realpath ..) include $(TOPDIR)/Make.version include $(TOPDIR)/Make.rules include $(TOPDIR)/Make.defaults .%.d : %.c $(CC) $(cflags) $(CPPFLAGS) -MM -MG -MF $@ $^ .%.d : %.S $(CC) $(cflags) $(CPPFLAGS) -MM -MG -MF $@ $^ SOURCES ?= deps : $(call deps-of,$(filter-out %.h,$(SOURCES))) efibootmgr-17/Make.rules000066400000000000000000000026731330730303200153730ustar00rootroot00000000000000default : all .PHONY: default all deps clean install include $(TOPDIR)/Make.version all : deps deps : clean : install : %.a : %.so $(AR) -cvqs $@ $^ % : %.c % : | %.c $(CCLD) $(ccldflags) $(CPPFLAGS) -o $@ $^ $(LDLIBS) %-static : %.c $(CCLD) $(ccldflags) $(CPPFLAGS) -o $@ $^ $(LDLIBS) %.so : $(CCLD) $(ccldflags) $(CPPFLAGS) $(SOFLAGS) \ -Wl,-soname,$@.$(VERSION) \ -o $@ $^ $(LDLIBS) %.o : %.c $(CC) $(cflags) $(CPPFLAGS) -c -o $@ $(filter %.c %.o %.S,$^) %.S: %.c $(CC) $(cflags) $(CPPFLAGS) -S $< -o $@ %.E: %.c $(CC) $(cflags) $(CPPFLAGS) -E $< -o $@ %.c : %.h .%.d : define substitute-version = sed \ -e "s,@@VERSION@@,$(VERSION),g" \ -e "s,@@LIBDIR@@,$(libdir),g" \ -e "s,@@LIBEXECDIR@@,$(libexecdir),g" \ $(1) > $(2) endef %.pc : %.pc.in @$(call substitute-version,$<,$@) %.spec : %.spec.in @$(call substitute-version,$<,$@) pkg-config-cflags = \ $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --cflags $(PKGS); fi) pkg-config-ldflags = \ $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --libs-only-L --libs-only-other $(PKGS) ; fi) pkg-config-ldlibs = \ $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --libs-only-l $(PKGS) ; fi) objects-of = \ $(patsubst %.c,%.o,$(1)) define deps-of = $(foreach src,$(filter %.c,$(1)),$(patsubst %.c,.%.d,$(src))) \ $(foreach src,$(filter %.S,$(1)),$(patsubst %.S,.%.d,$(src))) endef define get-config = $(shell git config --local --get "efibootmgr.$(1)") endef efibootmgr-17/Make.scan-build000066400000000000000000000011011330730303200162430ustar00rootroot00000000000000SCAN_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions scan-build 2>/dev/null) ; [ -n "$$x" ] && echo 1) ifeq ($(SCAN_BUILD),) SCAN_BUILD_ERROR = $(error scan-build not found) endif scan-test : ; $(SCAN_BUILD_ERROR) scan-clean : clean @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi scan-build : | scan-test scan-build : clean scan-build -o scan-results make $(DASHJ) CC=clang all scan-build-all : | scan-test scan-build-all : clean scan-build -o scan-results make $(DASHJ) CC=clang all .PHONY : scan-build scan-clean efibootmgr-17/Make.version000066400000000000000000000000131330730303200157100ustar00rootroot00000000000000VERSION=17 efibootmgr-17/Makefile000066400000000000000000000037741330730303200151050ustar00rootroot00000000000000TOPDIR = $(shell echo $$PWD) include $(TOPDIR)/Make.version include $(TOPDIR)/Make.rules include $(TOPDIR)/Make.defaults include $(TOPDIR)/Make.coverity include $(TOPDIR)/Make.scan-build SUBDIRS := src all install deps : | check_efidir_error Make.version @set -e ; for x in $(SUBDIRS) ; do \ $(MAKE) -C $$x $@ ; \ done clean : | check_efidir_error Make.version @set -e ; for x in $(SUBDIRS) ; do \ $(MAKE) -C $$x $@ ; \ done all : efibootmgr.spec efibootmgr efibootmgr-static : $(MAKE) -C src $@ $(SUBDIRS) : $(MAKE) -C $@ .PHONY: $(SUBDIRS) efibootmgr.spec : | Makefile Make.version distclean : $(MAKE) clean @rm -vf efibootmgr.spec GITTAG = $(shell bash -c "echo $$(($(VERSION) + 1))") test-archive: efibootmgr.spec @rm -rf /tmp/efibootmgr-$(GITTAG) /tmp/efibootmgr-$(GITTAG)-tmp @mkdir -p /tmp/efibootmgr-$(GITTAG)-tmp @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; tar x ) @git diff | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; patch -s -p1 -b -z .gitdiff ) @mv /tmp/efibootmgr-$(GITTAG)-tmp/ /tmp/efibootmgr-$(GITTAG)/ @cp efibootmgr.spec /tmp/efibootmgr-$(GITTAG)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/efibootmgr-$(GITTAG).tar.bz2 efibootmgr-$(GITTAG) @rm -rf /tmp/efibootmgr-$(GITTAG) @echo "The archive is in efibootmgr-$(GITTAG).tar.bz2" bumpver : @echo VERSION=$(GITTAG) > Make.version @git add Make.version git commit -m "Bump version to $(GITTAG)" -s tag: git tag -s $(GITTAG) refs/heads/master archive: bumpver tag efibootmgr.spec @rm -rf /tmp/efibootmgr-$(GITTAG) /tmp/efibootmgr-$(GITTAG)-tmp @mkdir -p /tmp/efibootmgr-$(GITTAG)-tmp @git archive --format=tar $(GITTAG) | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; tar x ) @mv /tmp/efibootmgr-$(GITTAG)-tmp/ /tmp/efibootmgr-$(GITTAG)/ @cp efibootmgr.spec /tmp/efibootmgr-$(GITTAG)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/efibootmgr-$(GITTAG).tar.bz2 efibootmgr-$(GITTAG) @rm -rf /tmp/efibootmgr-$(GITTAG) @echo "The archive is in efibootmgr-$(GITTAG).tar.bz2" efibootmgr-17/README000066400000000000000000000105701330730303200143150ustar00rootroot00000000000000This is efibootmgr, a Linux user-space application to modify the Intel Extensible Firmware Interface (EFI) Boot Manager. This application can create and destroy boot entries, change the boot order, change the next running boot option, and more. Details on the EFI Boot Manager are available from the EFI Specification, v1.02 or above, available from: http://www.uefi.org Note: efibootmgr requires that the kernel module efivars be loaded prior to use. Running 'modprobe efivars' should do the trick. usage: efibootmgr [options] -a | --active sets bootnum active -A | --inactive sets bootnum inactive -b | --bootnum XXXX modify BootXXXX (hex) -B | --delete-bootnum delete bootnum -c | --create create new variable bootnum and add to bootorder -d | --disk disk (defaults to /dev/sda) containing loader -e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess -E | --device num EDD 1.0 device number (defaults to 0x80) -g | --gpt force disk w/ invalid PMBR to be treated as GPT -i | --iface name create a netboot entry for the named interface -l | --loader name (defaults to \elilo.efi) -L | --label label Boot manager display label (defaults to "Linux") -n | --bootnext XXXX set BootNext to XXXX (hex) -N | --delete-bootnext delete BootNext -o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex) -O | --delete-bootorder delete BootOrder -p | --part part (defaults to 1) containing loader -q | --quiet be quiet -t | --timeout seconds Boot manager timeout -T | --delete-timeout delete Timeout value -u | --unicode | --UCS-2 pass extra args as UCS-2 (default is ASCII) -v | --verbose print additional information -V | --version return version and exit -w | --write-signature write unique sig to MBR if needed -@ | --append-binary-args append extra variable args from file (use - to read from stdin). Typical usage: Root can use it to display the current Boot Manager settings. [root@localhost ~]# efibootmgr BootCurrent: 0004 BootNext: 0003 BootOrder: 0004,0000,0001,0002,0003 Timeout: 30 seconds Boot0000* Diskette Drive(device:0) Boot0001* CD-ROM Drive(device:FF) Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) Boot0003* PXE Boot: MAC(00D0B7C15D91) Boot0004* Linux This shows: BootCurrent - the boot entry used to start the currently running system. BootOrder - the boot order as would appear in the boot manager. The boot manager tries to boot the first active entry on this list. If unsuccessful, it tries the next entry, and so on. BootNext - the boot entry which is scheduled to be run on next boot. This superceeds BootOrder for one boot only, and is deleted by the boot manager after first use. This allows you to change the next boot behavior without changing BootOrder. Timeout - the time in seconds between when the boot manager appears on the screen until when it automatically chooses the startup value from BootNext or BootOrder. Five boot entries (0000 - 0004), the active/inactive flag (* means active), and the name displayed on the screen. Alternative use cases could be as follows: 1) An OS installer would call 'efibootmgr -c'. This assumes that /boot/efi is your EFI System Partition, and is mounted at /dev/sda1. This creates a new boot option, called "Linux", and puts it at the top of the boot order list. Options may be passed to modify the default behavior. The default OS Loader is elilo.efi. 2) A system administrator wants to change the boot order. She would call 'efibootmgr -o 3,4' to specify PXE boot first, then Linux boot. 3) A system administrator wants to change the boot order for the next boot only. She would call 'efibootmgr -n 4' to specify that the Linux entry be taken on next boot. 4) A system administrator wants to delete the Linux boot option from the menu. 'efibootmgr -b 4 -B' deletes entry 4 and removes it from BootOrder. 5) A system administrator wants to create a boot option to network boot (PXE). You create the boot entry with: 'efibootmgr -c -i eth0 -L netboot' Please direct any bugs, features, patches, etc. to Peter Jones: https://github.com/rhboot/efibootmgr efibootmgr-17/README.md000066400000000000000000000106461330730303200147200ustar00rootroot00000000000000This is **efibootmgr**, a Linux user-space application to modify the Intel Extensible Firmware Interface (EFI) Boot Manager. This application can create and destroy boot entries, change the boot order, change the next running boot option, and more. Details on the EFI Boot Manager are available from the EFI Specification, v1.02 or above, available from: http://www.uefi.org Note: efibootmgr requires that the kernel module efivars be loaded prior to use. Running `modprobe efivars` should do the trick. ``` usage: efibootmgr [options] -a | --active sets bootnum active -A | --inactive sets bootnum inactive -b | --bootnum XXXX modify BootXXXX (hex) -B | --delete-bootnum delete bootnum -c | --create create new variable bootnum and add to bootorder -d | --disk disk (defaults to /dev/sda) containing loader -e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess -E | --device num EDD 1.0 device number (defaults to 0x80) -g | --gpt force disk w/ invalid PMBR to be treated as GPT -i | --iface name create a netboot entry for the named interface -l | --loader name (defaults to \elilo.efi) -L | --label label Boot manager display label (defaults to "Linux") -n | --bootnext XXXX set BootNext to XXXX (hex) -N | --delete-bootnext delete BootNext -o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex) -O | --delete-bootorder delete BootOrder -p | --part part (defaults to 1) containing loader -q | --quiet be quiet -t | --timeout seconds Boot manager timeout -T | --delete-timeout delete Timeout value -u | --unicode | --UCS-2 pass extra args as UCS-2 (default is ASCII) -v | --verbose print additional information -V | --version return version and exit -w | --write-signature write unique sig to MBR if needed -@ | --append-binary-args append extra variable args from file (use - to read from stdin). ``` Typical usage: Root can use it to display the current Boot Manager settings. [root@localhost ~]# efibootmgr BootCurrent: 0004 BootNext: 0003 BootOrder: 0004,0000,0001,0002,0003 Timeout: 30 seconds Boot0000* Diskette Drive(device:0) Boot0001* CD-ROM Drive(device:FF) Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) Boot0003* PXE Boot: MAC(00D0B7C15D91) Boot0004* Linux This shows: **BootCurrent** - the boot entry used to start the currently running system. **BootOrder** - the boot order as would appear in the boot manager. The boot manager tries to boot the first active entry on this list. If unsuccessful, it tries the next entry, and so on. **BootNext** - the boot entry which is scheduled to be run on next boot. This superceeds BootOrder for one boot only, and is deleted by the boot manager after first use. This allows you to change the next boot behavior without changing BootOrder. **Timeout** - the time in seconds between when the boot manager appears on the screen until when it automatically chooses the startup value from BootNext or BootOrder. Five boot entries (0000 - 0004), the active/inactive flag (* means active), and the name displayed on the screen. Alternative use cases could be as follows: 1) An OS installer would call `efibootmgr -c`. This assumes that /boot/efi is your EFI System Partition, and is mounted at /dev/sda1. This creates a new boot option, called "Linux", and puts it at the top of the boot order list. Options may be passed to modify the default behavior. The default OS Loader is elilo.efi. 2) A system administrator wants to change the boot order. She would call `efibootmgr -o 3,4` to specify PXE boot first, then Linux boot. 3) A system administrator wants to change the boot order for the next boot only. She would call `efibootmgr -n 4` to specify that the Linux entry be taken on next boot. 4) A system administrator wants to delete the Linux boot option from the menu. `efibootmgr -b 4 -B` deletes entry 4 and removes it from BootOrder. 5) A system administrator wants to create a boot option to network boot (PXE). You create the boot entry with: `efibootmgr -c -i eth0 -L netboot` Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr . efibootmgr-17/TODO000066400000000000000000000010251330730303200141200ustar00rootroot00000000000000- support for setting hotkeys - support for driver variables, not just boot variables. - support for arbitrary device paths with -c (including guid parsing) - maintain a list of known GUIDs and print {vendor} instead of the full GUID - accept these for arbitrary paths as well. - these are done in libefivar, but efibootmgr still needs some work on it - MS-DOS style extended partitions - lots more network stuff - IPv6 with various discovery methods - IPv4 w/o dhcp - make sure FCoE works - iscsi - make sure nvme works efibootmgr-17/efibootmgr.spec.in000066400000000000000000000064001330730303200170500ustar00rootroot00000000000000Summary: EFI Boot Manager Name: efibootmgr Version: @@VERSION@@ Release: 1%{?dist} Group: System Environment/Base License: GPLv2+ URL: https://github.com/rhboot/%{name}/ BuildRequires: git, popt-devel BuildRequires: efivar-libs >= 30-1, efivar-devel >= 30-1 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXXXX) # EFI/UEFI don't exist on PPC ExclusiveArch: %{ix86} x86_64 aarch64 arm # for RHEL / Fedora when efibootmgr was part of the elilo package Conflicts: elilo <= 3.6-6 Obsoletes: elilo <= 3.6-6 Source0: https://github.com/rhboot/%{name}/releases/download/%{name}-%{version}/%{name}-%{version}.tar.bz2 %description %{name} displays and allows the user to edit the UEFI Boot Manager variables. Additional information about UEFI can be found at http://www.uefi.org %prep %setup -q git init git config user.email "example@example.com" git config user.name "RHEL Ninjas" git add . git commit -a -q -m "%{version} baseline." git am %{patches} - 14-1 - Update to efibootmgr 14 - Remove "(hex)" from description of --delete-bootnum - Fix a typo in the popt options - Add README.md - make efibootdump install by default - Man page fixes - Better compiler detection - Don't use --default-symver in efibootmgr - Make -flto part of the overrideable CFLAGS * Wed Aug 17 2016 Peter Jones - 13-1 - Update to efibootmgr 13 - Add support for --sysprep and --driver to support UEFI System Prep Applications and UEFI Drivers. - use efivar's error reporting facility, and show error traces when "-v -v" is used. - Still yet better error codes returned on failures. - Add -m and -M to support Memory Address Range Mirroring. - Add efibootdump, to examine Boot* variables found in tarballs in bug reports and similar. - miscellaneous bugfixes. * Thu May 28 2015 Peter Jones - 0.12-1 - Update to 0.12 - use libefiboot and libefivar to make device paths and load options - don't depend on -lz or -lpci any more * Tue Oct 21 2014 Peter Jones - 0.11.0-1 - Fix "-n" and friends not being assigned/checked right sometimes from 0.10.0-1 - Generate more archives to avoid people using github's, because they're just bad. * Mon Oct 20 2014 Peter Jones - 0.10.0-1 - Make -o parameter validation work better and be more informative - Better exit values - Fix a segfault with appending ascii arguments. * Tue Sep 09 2014 Peter Jones - 0.8.0-1 - Release 0.8.0 * Mon Jan 13 2014 Peter Jones - 0.6.1-1 - Release 0.6.1 * Mon Jan 13 2014 Jared Dominguez - new home https://github.com/vathpela/efibootmgr * Thu Jan 3 2008 Matt Domsch 0.5.4-1 - split efibootmgr into its own RPM for Fedora/RHEL. * Tue Aug 24 2004 Matt Domsch - new home linux.dell.com * Fri May 18 2001 Matt Domsch - See doc/ChangeLog efibootmgr-17/src/000077500000000000000000000000001330730303200142215ustar00rootroot00000000000000efibootmgr-17/src/.gitignore000066400000000000000000000000551330730303200162110ustar00rootroot00000000000000efibootmgr eficonman efibootnext efibootdump efibootmgr-17/src/Makefile000066400000000000000000000026661330730303200156730ustar00rootroot00000000000000SRCDIR = $(realpath .) TOPDIR = $(realpath ..) include $(TOPDIR)/Make.version include $(TOPDIR)/Make.rules include $(TOPDIR)/Make.defaults SUBDIR_CFLAGS = -I$(SRCDIR)/include BINTARGETS=efibootmgr efibootdump TARGETS=$(BINTARGETS) all : deps $(TARGETS) EFIBOOTMGR_SOURCES = efibootmgr.c efi.c unparse_path.c EFICONMAN_SOURCES = eficonman.c EFIBOOTDUMP_SOURCES = efibootdump.c unparse_path.c EFIBOOTNEXT_SOURCES = efibootnext.c ALL_SOURCES=$(EFIBOOTMGR_SOURCES) -include $(call deps-of,$(ALL_SOURCES)) efibootmgr : $(call objects-of,$(EFIBOOTMGR_SOURCES)) efibootmgr : PKGS=efivar efiboot eficonman : $(call objects-of,$(EFICONMAN_SOURCES)) eficonman : PKGS=efivar efiboot popt efibootdump : $(call objects-of,$(EFIBOOTDUMP_SOURCES)) efibootdump : PKGS=efivar efiboot popt efibootnext : $(call objects-of,$(EFIBOOTNEXT_SOURCES)) efibootnext : PKGS=efivar efiboot popt deps : $(ALL_SOURCES) $(MAKE) -f $(TOPDIR)/Make.deps deps SOURCES="$(ALL_SOURCES)" SUBDIR_CFLAGS="$(SUBDIR_CFLAGS)" clean : @rm -rfv *.o *.a *.so $(TARGETS) @rm -rfv .*.d install : $(INSTALL) -d -m 755 $(DESTDIR)/$(sbindir)/ $(INSTALL) -m 755 efibootmgr $(DESTDIR)/$(sbindir)/efibootmgr $(INSTALL) -m 755 efibootdump $(DESTDIR)/$(sbindir)/efibootdump $(INSTALL) -d -m 755 $(DESTDIR)/$(mandir)/man8/ $(INSTALL) -m 644 efibootmgr.8 $(DESTDIR)/$(mandir)/man8/efibootmgr.8 $(INSTALL) -m 644 efibootdump.8 $(DESTDIR)/$(mandir)/man8/efibootdump.8 .PHONY : all deps clean install efibootmgr-17/src/efi.c000066400000000000000000000300631330730303200151320ustar00rootroot00000000000000/* efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars Copyright (C) 2001,2003 Dell Computer Corporation 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "fix_coverity.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "efi.h" #include "efibootmgr.h" #include "list.h" static int select_var_names_by_prefix(const efi_guid_t *guid, const char *prefix, const char *name) { efi_guid_t global = EFI_GLOBAL_GUID; size_t plen = strlen(prefix); const char *num = name + plen; if (!strncmp(name, prefix, plen) && isxdigit(num[0]) && isxdigit(num[1]) && isxdigit(num[2]) && isxdigit(num[3]) && !memcmp(guid, &global, sizeof (global))) return 1; return 0; } typedef __typeof__(select_var_names_by_prefix) filter_t; static int cmpstringp(const void *p1, const void *p2) { const char *s1 = *(const char **)p1; const char *s2 = *(const char **)p2; return strcoll(s1, s2); } static int read_prefixed_var_names(filter_t filter, const char *prefix, char ***namelist) { int rc; efi_guid_t *guid = NULL; char *name = NULL; char **newlist = NULL; int nentries = 0; int i; rc = efi_variables_supported(); if (!rc) return -1; while ((rc = efi_get_next_variable_name(&guid, &name)) > 0) { if (!filter(guid, prefix, name)) continue; char *aname = strdup(name); if (!aname) { rc = -1; break; } char **tmp = realloc(newlist, (++nentries + 1) * sizeof (*newlist)); if (!tmp) { free(aname); rc = -1; break; } tmp[nentries] = NULL; tmp[nentries-1] = aname; newlist = tmp; } if (rc == 0 && newlist) { qsort(newlist, nentries, sizeof (char *), cmpstringp); *namelist = newlist; } else { if (newlist) { for (i = 0; newlist[i] != NULL; i++) free(newlist[i]); free(newlist); } } return rc; } int read_var_names(const char *prefix, char ***namelist) { return read_prefixed_var_names(select_var_names_by_prefix, prefix, namelist); } int read_boot_var_names(char ***namelist) { return read_var_names("Boot", namelist); } #if 0 static int get_virt_pci(char *name, unsigned char *bus, unsigned char *device, unsigned char *function) { char inbuf[64], outbuf[128]; ssize_t lnksz; if (snprintf(inbuf, sizeof inbuf, "/sys/bus/virtio/devices/%s", name) >= (ssize_t)(sizeof inbuf)) { return -1; } lnksz = readlink(inbuf, outbuf, sizeof outbuf); if (lnksz == -1 || lnksz == sizeof outbuf) { return -1; } outbuf[lnksz] = '\0'; if (sscanf(outbuf, "../../../devices/pci0000:00/0000:%hhx:%hhx.%hhx", bus, device, function) != 3) { return -1; } return 0; } /** * make_net_load_option() * @iface - interface name (input) * @buf - buffer to write structure to * @size - size of buf * * Returns -1 on error, size written on success, or size needed if size == 0. */ static ssize_t make_net_load_option(char *iface, uint8_t *buf, size_t size) { /* copied pretty much verbatim from the ethtool source */ int fd = 0, err; unsigned char bus, slot, func; struct ifreq ifr; struct ethtool_drvinfo drvinfo; ssize_t needed; off_t buf_offset; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, iface); drvinfo.cmd = ETHTOOL_GDRVINFO; ifr.ifr_data = (caddr_t)&drvinfo; /* Open control socket */ fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("Cannot get control socket"); return -1; } err = ioctl(fd, SIOCETHTOOL, &ifr); if (err < 0) { perror("Cannot get driver information"); close(fd); return -1; } if (strncmp(drvinfo.bus_info, "virtio", 6) == 0) { err = get_virt_pci(drvinfo.bus_info, &bus, &slot, &func); if (err < 0) { close(fd); return err; } } else { /* The domain part was added in 2.6 kernels. * Test for that first. */ err = sscanf(drvinfo.bus_info, "%*x:%hhx:%hhx.%hhx", &bus, &slot, &func); if (err != 3) { err = sscanf(drvinfo.bus_info, "%hhx:%hhx.%hhx", &bus, &slot, &func); if (err != 3) { perror("Couldn't parse device location string."); close(fd); return -1; } } } err = ioctl(fd, SIOCGIFHWADDR, &ifr); if (err < 0) { close(fd); perror("Cannot get hardware address."); return -1; } buf_offset = 0; needed = efidp_make_acpi_hid(buf, size?size-buf_offset:0, opts.acpi_hid, opts.acpi_uid); if (needed < 0) { err_needed: close(fd); return needed; } buf_offset += needed; needed = make_pci_device_path(bus, (uint8_t)slot, (uint8_t)func, buf + buf_offset, size == 0 ? 0 : size - buf_offset); if (needed < 0) goto err_needed; buf_offset += needed; needed = efidp_make_mac_addr(buf, size?size-buf_offset:0, ifr.ifr_ifru.ifru_hwaddr.sa_family, (uint8_t*)ifr.ifr_ifru.ifru_hwaddr.sa_data, sizeof (ifr.ifr_ifru.ifru_hwaddr.sa_data)); if (needed < 0) goto err_needed; buf_offset += needed; if (opts.ipv4) { needed = make_ipv4_addr_device_path(fd, ); if (needed < 0) goto err_needed; buf_offset += needed; } if (opts.ipv6) { needed = make_ipv6_addr_device_path(fd, ); if (needed < 0) goto err_needed; buf_offset += needed; } close(fd); needed = efidp_make_end_entire(buf,size?size-buf_offset:0); if (needed < 0) return needed; buf_offset += needed; return buf_offset; } #endif static int get_edd_version(void) { efi_guid_t guid = BLKX_UNKNOWN_GUID; uint8_t *data = NULL; size_t data_size = 0; uint32_t attributes; efidp_header *path; int rc = 0; /* Allow global user option override */ switch (opts.edd_version) { case 0: /* No EDD information */ return 0; case 1: /* EDD 1.0 */ return 1; case 3: /* EDD 3.0 */ return 3; default: break; } rc = efi_get_variable(guid, "blk0", &data, &data_size, &attributes); if (rc < 0) return rc; path = (efidp_header *)data; if (path->type == 2 && path->subtype == 1) return 3; return 1; } /** * make_linux_load_option() * @data - load option returned * *data_size - load option size returned * * Returns 0 on error, length of load option created on success. */ ssize_t make_linux_load_option(uint8_t **data, size_t *data_size, uint8_t *optional_data, size_t optional_data_size) { ssize_t needed; uint32_t attributes = opts.active ? LOAD_OPTION_ACTIVE : 0; int saved_errno; efidp dp = NULL; if (opts.iface && opts.ip_version == EFIBOOTMGR_IPV4) { needed = efi_generate_ipv4_device_path(NULL, 0, opts.iface, opts.local_ip_addr, opts.remote_ip_addr, opts.gateway_ip_addr, opts.ip_netmask, opts.ip_local_port, opts.ip_remote_port, opts.ip_protocol, opts.ip_addr_origin); if (needed < 0) { efi_error("efi_generate_ipv4_device_path() = %zd (failed)", needed); return -1; } if (data_size && *data_size) { dp = malloc(needed); needed = efi_generate_ipv4_device_path( (uint8_t *)dp, needed, opts.iface, opts.local_ip_addr, opts.remote_ip_addr, opts.gateway_ip_addr, opts.ip_netmask, opts.ip_local_port, opts.ip_remote_port, opts.ip_protocol, opts.ip_addr_origin); if (needed < 0) { free(dp); efi_error("efi_generate_ipv4_device_path() = %zd (failed)", needed); return -1; } } } else if (opts.iface && opts.ip_version == EFIBOOTMGR_IPV6) { errno = ENOSYS; return -1; } else { uint32_t options = EFIBOOT_ABBREV_HD; int edd; /* there's really no telling if this is even the right disk, * but... I also never see blk0 exported to runtime on any * hardware, so it probably only happens on some old itanium * box from the beginning of time anyway. */ edd = get_edd_version(); switch (edd) { case 1: options = EFIBOOT_ABBREV_EDD10; break; case 3: options = EFIBOOT_ABBREV_NONE; break; } needed = efi_generate_file_device_path_from_esp(NULL, 0, opts.disk, opts.part, opts.loader, options, opts.edd10_devicenum); if (needed < 0) { efi_error("efi_generate_file_device_path_from_esp() = %zd (failed)", needed); return -1; } if (data_size && *data_size) { dp = malloc(needed); if (dp == NULL) return -1; needed = efi_generate_file_device_path_from_esp( (uint8_t *)dp, needed, opts.disk, opts.part, opts.loader, options, opts.edd10_devicenum); if (needed < 0) { efi_error("efi_generate_file_device_path_from_esp() = %zd (failed)", needed); free(dp); return -1; } } } size_t data_size_tmp = 0; if (data_size) data_size_tmp = *data_size; needed = efi_loadopt_create(*data, data_size_tmp, attributes, dp, needed, opts.label, optional_data, optional_data_size); if (dp) { saved_errno = errno; free(dp); dp = NULL; errno = saved_errno; } if (needed < 0) { efi_error("efi_loadopt_create() = %zd (failed)", needed); return -1; } return needed; } static ssize_t read_stdin(uint8_t *data_out, ssize_t data_size_out) { static uint8_t *data = NULL; static ssize_t data_size = 0; off_t pos = 0; ssize_t allocated; if (data_out && data_size_out) { if (!data || data_size != data_size_out) { errno = EINVAL; return -1; } memcpy(data_out, data, data_size); return data_size; } allocated = 4096; data = malloc(allocated); if (!data) return -1; memset(data, 0, allocated); while (1) { ssize_t ret; if (allocated - pos == 0) { allocated += 4096; /* * there's really no way a variable is going to be * 64k and work, so bail before we suck up all of * memory. */ if (allocated > 4096 * 16) { errno = ENOSPC; err: free(data); data = 0; data_size = 0; return -1; } uint8_t *data_new; data_new = realloc(data, allocated); if (!data_new) goto err; data = data_new; } ret = fread(data+pos, 1, allocated-pos, stdin); if (ret == 0) { if (ferror(stdin)) { errno = EIO; goto err; } if (feof(stdin)) break; } data_size += ret; pos += ret; } return data_size; } ssize_t get_extra_args(uint8_t *data, ssize_t data_size) { int i; ssize_t needed = 0, sz; off_t off = 0; if (opts.extra_opts_file) { if (!strcmp(opts.extra_opts_file, "-")) needed = read_stdin(data, data_size); else needed = efi_loadopt_args_from_file(data, data_size, opts.extra_opts_file); if (needed < 0) fprintf(stderr, "efibootmgr: get_extra_args: %m\n"); return needed; } for (i = opts.optind; i < opts.argc; i++) { int space = (i < opts.argc - 1) ? 1 : 0; if (opts.unicode) { sz = efi_loadopt_args_as_ucs2( (uint16_t *)(data+off), data_size?data_size+off:0, (uint8_t *)opts.argv[i]); if (sz < 0) return -1; off += sz; if (data && off < data_size-2 && space) { data[off] = '\0'; data[off+1] = '\0'; } off += space * sizeof (uint16_t); } else { sz = efi_loadopt_args_as_utf8(data+off, data_size?data_size+off:0, (uint8_t *)opts.argv[i]); if (sz < 0) return -1; off += sz; if (data && off < data_size-1 && space) { data[off] = '\0'; } off += space; } needed += off; } return needed; } efibootmgr-17/src/efibootdump.8000066400000000000000000000021451330730303200166310ustar00rootroot00000000000000.TH "EFIBOOTDUMP" "8" "24 February 2016" "" "" .SH NAME efibootdump \- dump a boot entries from a variable or a file .SH SYNOPSIS \fBefibootdump\fR [\fB-?\fR|\fB--help\fR] [\fB--usage\fR] .br [\fB-f\fR \fI\fR [... \fB-f\fR \fI\fR]] .br [[\fB-g\fR \fI{guid}\fR] \fI\fR [... [\fI\fR]]] .SH "DESCRIPTION" .PP \fBefibootdump\fR is a userspace application used to display individual UEFI boot options, from a file or a UEFI variable. This allows e.g. saved files from efivarfs to be displayed, as well as variables on the running machine. .SH "OPTIONS" The following is a list of options accepted by efibootmgr: .TP \fB-g | --guid\fR \fI{guid}\fR Any variables specified by name have the specified GUID. .TP \fB-f | --file\fR \fI\fR Read a single boot variable from the specified file. .TP \fI\fR Display the specified variable on the local machine. If no GUID is specified, EFI Global Variable is the default. .SH "BUGS" .PP Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr \&. .SH "SEE ALSO" .PP efibootmgr(8) efibootmgr-17/src/efibootdump.c000066400000000000000000000151551330730303200167110ustar00rootroot00000000000000/* * efibootdump.c - dump a variable as if it's a Boot#### variable * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #include "fix_coverity.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "unparse_path.h" int verbose; #define _(String) gettext (String) #define Q_(String) dgettext (NULL, String) #define C_(Context,String) dgettext (Context,String) static void print_boot_entry(efi_load_option *loadopt, size_t data_size) { char *text_path = NULL; size_t text_path_len = 0; uint8_t *optional_data = NULL; size_t optional_data_len = 0; uint16_t pathlen; const unsigned char *desc; char *raw; size_t raw_len; ssize_t rc; efidp dp = NULL; printf("%c ", (efi_loadopt_attrs(loadopt) & LOAD_OPTION_ACTIVE) ? '*' : ' '); desc = efi_loadopt_desc(loadopt, data_size); if (!desc) printf(" "); else if (desc[0]) printf("%s ", desc); dp = efi_loadopt_path(loadopt, data_size); pathlen = efi_loadopt_pathlen(loadopt, data_size); rc = efidp_format_device_path(NULL, 0, dp, pathlen); if (rc < 0) { printf(""); return; } text_path_len = rc + 1; text_path = alloca(text_path_len); if (!text_path) error(100, "Couldn't allocate memory"); rc = efidp_format_device_path(text_path, text_path_len, dp, pathlen); if (rc < 0) { printf(""); return; } if (text_path && text_path_len >= 1) printf("%s", text_path); rc = efi_loadopt_optional_data(loadopt, data_size, &optional_data, &optional_data_len); if (rc < 0) { printf(""); return; } rc = unparse_raw_text(NULL, 0, optional_data, optional_data_len); if (rc < 0) { printf(""); return; } raw_len = rc + 1; raw = alloca(raw_len); if (!raw) error(101, "Couldn't allocate memory"); rc = unparse_raw_text(raw, raw_len, optional_data, optional_data_len); if (rc < 0) { printf(""); } else if (rc > 0) { for (unsigned int i = 0; i < optional_data_len; i++) putchar(isprint(optional_data[i]) ? optional_data[i] : '.'); } printf("\n"); } int main(int argc, char *argv[]) { const char **names = NULL; const char **files = NULL; char *guidstr = NULL; efi_guid_t guid = efi_guid_global; setlocale(LC_ALL, ""); bindtextdomain("efibootdump", LOCALEDIR); textdomain("efibootdump"); struct poptOption options[] = { {.argInfo = POPT_ARG_INTL_DOMAIN, .arg = "efibootdump" }, {.longName = "guid", .shortName = 'g', .argInfo = POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL | POPT_ARGFLAG_STRIP, .arg = &guidstr, .descrip = _("GUID namespace the variable is in"), .argDescrip = "{guid}"}, {.longName = "file", .shortName = 'f', .argInfo = POPT_ARG_ARGV | POPT_ARGFLAG_OPTIONAL | POPT_ARGFLAG_STRIP, .arg = &files, .descrip = _("File to read variable data from"), .argDescrip = ""}, {.longName = "verbose", .shortName = 'v', .argInfo = POPT_ARG_VAL | POPT_ARGFLAG_OPTIONAL | POPT_ARGFLAG_STRIP, .arg = &verbose, .val = 2, .descrip = _("Be more verbose on errors"), }, POPT_AUTOALIAS POPT_AUTOHELP POPT_TABLEEND }; efi_load_option *loadopt; uint8_t *data = NULL; size_t data_size = 0; poptContext optcon; optcon = poptGetContext("efibootdump", argc, (const char **)argv, options, 0); poptSetOtherOptionHelp(optcon, "[OPTIONS...] [name0 [... [nameN]]]"); int rc; rc = poptReadDefaultConfig(optcon, 0); if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) errorx(1, _("poptReadDefaultConfig failed: %s: %s"), poptBadOption(optcon, 0), poptStrerror(rc)); while ((rc = poptGetNextOpt(optcon)) > 0) ; if (rc < -1) errorx(2, "Invalid argument: \"%s\": %s", poptBadOption(optcon, 0), poptStrerror(rc)); /* argc = */ poptStrippedArgv(optcon, argc, argv); names = poptGetArgs(optcon); if (!names && !files) { poptPrintUsage(optcon, stderr, 0); exit(4); } if (names && (!names[0] || names[0][0] == '\0')) { poptPrintUsage(optcon, stderr, 0); exit(4); } if (files && (!files[0] || files[0][0] == '\0')) { poptPrintUsage(optcon, stderr, 0); exit(4); } if (names) { if (guidstr) { rc = efi_id_guid_to_guid(guidstr, &guid); if (rc < 0) error(5, "Could not parse guid \"%s\"", guidstr); } free(guidstr); guidstr = NULL; rc = efi_guid_to_str(&guid, &guidstr); if (rc < 0) error(6, "Guid lookup failed"); } for (unsigned int i = 0; files != NULL && files[i] != NULL && files[i][0] != '\0'; i++) { struct stat statbuf; FILE *f; size_t n; const char *filename = files[i]; memset(&statbuf, 0, sizeof(statbuf)); rc = stat(filename, &statbuf); if (rc < 0) error(7, "Could not stat \"%s\"", filename); data_size = statbuf.st_size; if (data_size == 0) errorx(11, "File \"%s\" is empty", filename); data = alloca(data_size); if (data == NULL) error(8, "Could not allocate memory"); f = fopen(filename, "r"); if (!f) error(9, "Could not open \"%s\"", filename); n = fread(data, 1, data_size, f); if (n < data_size) error(10, "Could not read \"%s\"", filename); printf("%s: ", filename); loadopt = (efi_load_option *)(data + 4); if (data_size <= 8) errorx(11, "Data is not a valid load option"); if (efi_loadopt_is_valid(loadopt, data_size - 4)) { print_boot_entry(loadopt, data_size - 4); } else { loadopt = (efi_load_option *)data; if (!efi_loadopt_is_valid(loadopt, data_size)) errorx(11, "Data is not a valid load option"); print_boot_entry(loadopt, data_size); } fclose(f); } for (unsigned int i = 0; names && names[i] != NULL && names[i][0] != '\0'; i++) { uint32_t attrs = 0; rc = efi_get_variable(guid, names[i], &data, &data_size, &attrs); if (rc < 0) { warning("couldn't read variable %s-%s", names[i], guidstr); continue; } loadopt = (efi_load_option *)data; if (!efi_loadopt_is_valid(loadopt, data_size)) { warning("load option for %s is not valid", names[i]); printf("%d\n", __LINE__); if (data && data_size > 0) { free(data); continue; } } printf("%s", names[i]); if (efi_guid_cmp(&efi_guid_global, &guid)) printf("-%s", guidstr); printf(": "); print_boot_entry(loadopt, data_size); if (data && data_size > 0) free(data); } if (guidstr) free(guidstr); poptFreeContext(optcon); return 0; } efibootmgr-17/src/efibootmgr.8000066400000000000000000000150701330730303200164520ustar00rootroot00000000000000.TH "EFIBOOTMGR" "8" "26 December 2017" "" "" .SH NAME efibootmgr \- manipulate the UEFI Boot Manager .SH SYNOPSIS \fBefibootmgr\fR [ \fB-a\fR ] [ \fB-A\fR ] [ \fB-b \fIXXXX\fB\fR ] [ \fB-r\fR | \fB-y\fR ] [ \fB-B\fR ] [ \fB-c\fR ] [ \fB-d \fIDISK\fB\fR ] [ \fB-D\fR ] [ \fB-e \fI1|3|-1\fB\fR ] [ \fB-E \fINUM\fB\fR ] [ \fB-g\fR ] [ \fB-i \fINAME\fB\fR ] [ \fB-l \fINAME\fB\fR ] [ \fB-L \fILABEL\fB\fR ] [ \fB-m \fIt|f\fB\fR ] [ \fB-M \fIX\fB\fR ] [ \fB-n \fIXXXX\fB\fR ] [ \fB-N\fR ] [ \fB-o \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR\fI ...\fR ] [ \fB-O\fR ] [ \fB-p \fIPART\fB\fR ] [ \fB-q\fR ] [ \fB-t \fIseconds\fB\fR ] [ \fB-T\fR ] [ \fB-u\fR ] [ \fB-v\fR ] [ \fB-V\fR ] [ \fB-w\fR ] [ \fB-@ \fIfile\fB\fR ] .SH "DESCRIPTION" .PP \fBefibootmgr\fR is a userspace application used to modify the UEFI Boot Manager. This application can create and destroy boot entries, change the boot order, change the next running boot option, and more. .PP Details on the UEFI Boot Manager are available from the UEFI Specification, v1.02 or later, available from: http://www.uefi.org .sp .RS .B "Note:" efibootmgr requires that the kernel support access to EFI non-volatile variables through \fI/sys/firmware/efi/vars\fR or \fI/sys/firmware/efi/efivars/\fR. .RE .SH "OPTIONS" .PP The following is a list of options accepted by efibootmgr: .TP \fB-a | --active\fR Sets bootnum active .TP \fB-A | --inactive\fR Sets bootnum inactive .TP \fB-b | --bootnum \fIXXXX\fB\fR Modify Boot\fIXXXX\fR (hex) .TP \fB-B | --delete-bootnum\fR Delete bootnum .TP \fB-c | --create\fR Create new variable bootnum and add to bootorder .TP \fB-d | --disk \fIDISK\fB\fR The disk containing the loader (defaults to \fI/dev/sda\fR) .TP \fB-D | --remove-dups\fR Remove duplicated entries from BootOrder .TP \fB-e | --edd30 \fI1|3|-1\fB\fR Force EDD 1.0 or 3.0 creation variables, or guess. .TP \fB-E | --edd-device \fINUM\fB\fR EDD 1.0 device number (defaults to 0x80) .TP \fB-g | --gpt\fR Force disk with invalid PMBR to be treated as GPT .TP \fB-i | --iface \fINAME\fB\fR create a netboot entry for the named interface .TP \fB-l | --loader \fINAME\fB\fR Specify a loader (defaults to \fI\\\\elilo.efi\fR) .TP \fB-L | --label \fILABEL\fB\fR Boot manager display label (defaults to "Linux") .TP \fB-m | --mirror-below-4G \fIt|f\fB\fR Set t if you want to mirror memory below 4GB .TP \fB-M | --mirror-above-4G \fIX\fB\fR X percentage memory to mirror above 4GB. Floating-point value with up to 2 decimal places is accepted. .TP \fB-n | --bootnext \fIXXXX\fB\fR Set BootNext to XXXX (hex) .TP \fB-N | --delete-bootnext\fR Delete BootNext .TP \fB-o | --bootorder \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR Explicitly set BootOrder (hex). Any value from 0 to FFFF is accepted so long as it corresponds to an existing Boot#### variable, and zero padding is not required. .TP \fB-O | --delete-bootorder\fR Delete BootOrder .TP \fB-p | --part \fIPART\fB\fR Partition number containing the bootloader (defaults to 1) .TP \fB-q | --quiet\fR Quiet mode - supresses output. .TP \fB-r | --driver\fR Operate on Driver#### variables instead of Boot#### variables. .TP \fB-t | --timeout \fIseconds\fB\fR Boot Manager timeout, in \fIseconds\fR\&. .TP \fB-T | --delete-timeout\fR Delete Timeout variable. .TP \fB-u | --unicode | --UCS-2 \fR Handle extra command line arguments as UCS-2 (default is ASCII) .TP \fB-v | --verbose\fR Verbose mode - prints additional information .TP \fB-V | --version\fR Just print version string and exit. .TP \fB-w | --write-signature\fR write unique signature to the MBR if needed .TP \fB-y | --sysprep\fR Operate on SysPrep#### variables instead of Boot#### variables. .TP \fB-@ | --append-binary-args \fR append extra variable args from file (use - to read from stdin). Data in file is appended as command line arguments to the boot loader command, with no modification to the data, so you can pass any binary or text data necessary. .SH "EXAMPLES" \fR .SS "Displaying the current settings (must be root):" \fR .nf .B [root@localhost ~]# efibootmgr BootCurrent: 0004 BootNext: 0003 BootOrder: 0004,0000,0001,0002,0003 Timeout: 30 seconds Boot0000* Diskette Drive(device:0) Boot0001* CD-ROM Drive(device:FF) Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) Boot0003* PXE Boot: MAC(00D0B7C15D91) Boot0004* Linux .fi .PP Each of the above are boot variables, which are defined as follows: .RS .TP 0.2i \(bu BootCurrent - the boot entry used to start the currently running system .TP 0.2i \(bu BootOrder - the boot order as would appear in the boot manager. The boot manager tries to boot the first active entry in this list. If unsuccessful, it tries the next entry, and so on. .TP 0.2i \(bu BootNext - the boot entry which is scheduled to be run on next boot. This supercedes BootOrder for one boot only, and is deleted by the boot manager after first use. This allows you to change the next boot behavior without changing BootOrder. .TP 0.2i \(bu Timeout - the time in seconds between when the boot manager appears on the screen until when it automatically chooses the startup value from BootNext or BootOrder. .TP 0.2i \(bu Five boot entries (0000 - 0004), along with the active/inactive flag (* means active) and the name displayed on the screen. .RE .SS "Creating a new boot option" An OS installer would call \fBefibootmgr -c\fR\&. This assumes that \fI/boot/efi\fR is your EFI System Partition, and is mounted at \fI/dev/sda1\fR\&. This creates a new boot option, called "Linux", and puts it at the top of the boot order list. Options may be passed to modify the default behavior. The default OS Loader is \fIelilo.efi\fR\&. .SS "Changing the boot order" Assuming the configuration in the first example, \fBefibootmgr -o 3,4\fR could be called to specify PXE boot first, then Linux boot. .SS "Changing the boot order for the next boot only" Assuming the configuration in the first example, \fBefibootmgr -n 4\fR could be called to specify that the Linux entry be taken on next boot. .SS "Deleting a boot option" Assuming the configuration in the first example, \fBefibootmgr -b 4 -B\fR could be called to delete entry 4 and remove it from the BootOrder. .SS "Creating network boot entries" A system administrator wants to create a boot option to network boot. You create the boot entry with: \fBefibootmgr -c -i eth0 -L netboot [ -l '\\filename.efi' ]\fR .SH "BUGS" .PP Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr \&. .SH "AUTHOR" .PP This man page was generated by dann frazier for the Debian GNU/Linux operating system and updated by Robert Bisewski , but may be used by others. .SH "SEE ALSO" .PP elilo(1) efibootmgr-17/src/efibootmgr.c000066400000000000000000001237131330730303200165310ustar00rootroot00000000000000/* efibootmgr.c - Manipulates EFI variables as exported in /sys/firmware/efi/ efivars or vars (previously /proc/efi/vars) Copyright (C) 2001-2004 Dell, Inc. 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA This must tie the EFI_DEVICE_PATH to /boot/efi/EFI//.efi The EFI_DEVICE_PATH will look something like: ACPI device path, length 12 bytes Hardware Device Path, PCI, length 6 bytes Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ?? Media Device Path, Hard Drive, partition XX, length 30 bytes Media Device Path, File Path, length ?? End of Hardware Device Path, length 4 Arguments passed to elilo, as UCS-2 characters, length ?? */ #include "fix_coverity.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "efi.h" #include "unparse_path.h" #include "efibootmgr.h" #include "error.h" #ifndef EFIBOOTMGR_VERSION #define EFIBOOTMGR_VERSION "unknown (fix Makefile!)" #endif int verbose; typedef struct _var_entry { char *name; efi_guid_t guid; uint8_t *data; size_t data_size; uint32_t attributes; uint16_t num; list_t list; } var_entry_t; /* global variables */ static LIST_HEAD(entry_list); static LIST_HEAD(blk_list); efibootmgr_opt_t opts; static void free_vars(list_t *head) { list_t *pos, *n; var_entry_t *entry; list_for_each_safe(pos, n, head) { entry = list_entry(pos, var_entry_t, list); if (entry->name) free(entry->name); if (entry->data) free(entry->data); list_del(&(entry->list)); free(entry); } } static void read_vars(char **namelist, list_t *head) { var_entry_t *entry; int i, rc; if (!namelist) return; for (i=0; namelist[i] != NULL; i++) { if (namelist[i]) { entry = malloc(sizeof(var_entry_t)); if (!entry) { efi_error("malloc(%zd) failed", sizeof(var_entry_t)); goto err; } memset(entry, 0, sizeof(var_entry_t)); rc = efi_get_variable(EFI_GLOBAL_GUID, namelist[i], &entry->data, &entry->data_size, &entry->attributes); if (rc < 0) { warning("Skipping unreadable variable \"%s\"", namelist[i]); free(entry); continue; } /* latest apple firmware sets high bit which appears * invalid to the linux kernel if we write it back so * lets zero it out if it is set since it would be * invalid to set it anyway */ entry->attributes = entry->attributes & ~(1 << 31); entry->name = strdup(namelist[i]); if (!entry->name) { efi_error("strdup(\"%s\") failed", namelist[i]); goto err; } entry->guid = EFI_GLOBAL_GUID; list_add_tail(&entry->list, head); } } return; err: exit(1); } static void free_array(char **array) { int i; if (!array) return; for (i = 0; array[i] != NULL; i++) free(array[i]); free(array); } static int compare(const void *a, const void *b) { int rc = -1; uint16_t n1, n2; memcpy(&n1, a, sizeof(n1)); memcpy(&n2, b, sizeof(n2)); if (n1 < n2) rc = -1; if (n1 == n2) rc = 0; if (n1 > n2) rc = 1; return rc; } /* Return an available variable number, or -1 on failure. */ static int find_free_var(list_t *var_list) { int num_vars=0, i=0, found; uint16_t *vars, free_number; list_t *pos; var_entry_t *entry; list_for_each(pos, var_list) num_vars++; if (num_vars == 0) return 0; vars = calloc(1, sizeof(uint16_t) * num_vars); if (!vars) { efi_error("calloc(1, %zd) failed", sizeof(uint16_t) * num_vars); return -1; } list_for_each(pos, var_list) { entry = list_entry(pos, var_entry_t, list); vars[i] = entry->num; i++; } qsort(vars, i, sizeof(uint16_t), compare); found = 1; num_vars = i; for (free_number = 0; free_number < num_vars && found; free_number++) { found = 0; list_for_each(pos, var_list) { entry = list_entry(pos, var_entry_t, list); if (entry->num == free_number) { found = 1; break; } } if (!found) break; } if (found && num_vars) free_number = vars[num_vars-1] + 1; free(vars); return free_number; } static void warn_duplicate_name(list_t *var_list) { list_t *pos; var_entry_t *entry; efi_load_option *load_option; const unsigned char *desc; list_for_each(pos, var_list) { entry = list_entry(pos, var_entry_t, list); load_option = (efi_load_option *)entry->data; desc = efi_loadopt_desc(load_option, entry->data_size); if (!strcmp((char *)opts.label, (char *)desc)) warnx("** Warning ** : %s has same label %s", entry->name, opts.label); } } static var_entry_t * make_var(const char *prefix, list_t *var_list) { var_entry_t *entry = NULL; int free_number; list_t *pos; int rc; uint8_t *extra_args = NULL; ssize_t extra_args_size = 0; ssize_t needed=0, sz; if (opts.num == -1) { free_number = find_free_var(var_list); } else { list_for_each(pos, var_list) { entry = list_entry(pos, var_entry_t, list); if (entry->num == opts.num) errx(40, "Cannot create %s%04X: already exists.", prefix, opts.num); } free_number = opts.num; } if (free_number == -1) { efi_error("efibootmgr: no available %s variables", prefix); return NULL; } /* Create a new var_entry_t object and populate it. */ entry = calloc(1, sizeof(*entry)); if (!entry) { efi_error("calloc(1, %zd) failed", sizeof(*entry)); return NULL; } sz = get_extra_args(NULL, 0); if (sz < 0) { efi_error("get_extra_args() failed"); goto err; } extra_args_size = sz; entry->data = NULL; entry->data_size = 0; needed = make_linux_load_option(&entry->data, &entry->data_size, NULL, sz); if (needed < 0) { efi_error("make_linux_load_option() failed"); goto err; } entry->data_size = needed; entry->data = malloc(needed); if (!entry->data) { efi_error("malloc(%zd) failed", needed); goto err; } extra_args = entry->data + needed - extra_args_size; sz = get_extra_args(extra_args, extra_args_size); if (sz < 0) { efi_error("get_extra_args() failed"); goto err; } sz = make_linux_load_option(&entry->data, &entry->data_size, extra_args, extra_args_size); if (sz < 0) { efi_error("make_linux_load_option failed"); goto err; } entry->num = free_number; entry->guid = EFI_GLOBAL_GUID; rc = asprintf(&entry->name, "%s%04X", prefix, free_number); if (rc < 0) { efi_error("asprintf failed"); goto err; } entry->attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; rc = efi_set_variable(entry->guid, entry->name, entry->data, entry->data_size, entry->attributes, 0644); if (rc < 0) { efi_error("efi_set_variable failed"); goto err; } list_add_tail(&entry->list, var_list); return entry; err: if (entry) { if (entry->data) free(entry->data); if (entry->name) { efi_error("Could not set variable %s", entry->name); free(entry->name); } else { efi_error("Could not set variable"); } free(entry); } return NULL; } static int read_order(const char *name, var_entry_t **order) { int rc; var_entry_t *new = NULL, *bo = NULL; if (*order == NULL) { new = calloc(1, sizeof (**order)); if (!new) { efi_error("calloc(1, %zd) failed", sizeof (**order)); return -1; } *order = bo = new; } else { bo = *order; } rc = efi_get_variable(EFI_GLOBAL_GUID, name, &bo->data, &bo->data_size, &bo->attributes); if (rc < 0 && new != NULL) { efi_error("efi_get_variable failed"); free(new); *order = NULL; bo = NULL; } if (bo) { /* latest apple firmware sets high bit which appears invalid * to the linux kernel if we write it back so lets zero it out * if it is set since it would be invalid to set it anyway */ bo->attributes = bo->attributes & ~(1 << 31); } return rc; } static int set_u16(const char *name, uint16_t num) { return efi_set_variable(EFI_GLOBAL_GUID, name, (uint8_t *)&num, sizeof (num), EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0644); } static int add_to_order(const char *name, uint16_t num) { var_entry_t *order = NULL; uint64_t new_data_size; uint16_t *new_data, *old_data; int rc; rc = read_order(name, &order); if (rc < 0) { if (errno == ENOENT) rc = set_u16(name, num); return rc; } /* We've now got an array (in order->data) of the * order. First add our entry, then copy the old array. */ old_data = (uint16_t *)order->data; new_data_size = order->data_size + sizeof(uint16_t); new_data = malloc(new_data_size); if (!new_data) return -1; new_data[0] = num; memcpy(new_data+1, old_data, order->data_size); /* Now new_data has what we need */ free(order->data); order->data = (uint8_t *)new_data; order->data_size = new_data_size; rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, order->data_size, order->attributes, 0644); free(order->data); free(order); return rc; } static int remove_dupes_from_order(char *name) { var_entry_t *order = NULL; uint64_t new_data_size; uint16_t *new_data, *old_data; unsigned int old_i,new_i; int rc; rc = read_order(name, &order); if (rc < 0) { if (errno == ENOENT) rc = 0; return rc; } old_data = (uint16_t *)(order->data); /* Start with the same size */ new_data_size = order->data_size; new_data = malloc(new_data_size); if (!new_data) return -1; unsigned int old_max = order->data_size / sizeof(*new_data); for (old_i = 0, new_i = 0; old_i < old_max; old_i++) { int copies = 0; unsigned int j; for (j = 0; j < new_i; j++) { if (new_data[j] == old_data[old_i]) { copies++; break; } } if (copies == 0) { /* Copy this value */ new_data[new_i] = old_data[old_i]; new_i++; } } /* Adjust the size if we didn't copy everything. */ new_data_size = sizeof(new_data[0]) * new_i; /* Now new_data has what we need */ free(order->data); order->data = (uint8_t *)new_data; order->data_size = new_data_size; efi_del_variable(EFI_GLOBAL_GUID, name); rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, order->data_size, order->attributes, 0644); free(order->data); free(order); return rc; } static int remove_from_order(const char *name, uint16_t num) { var_entry_t *order = NULL; uint16_t *data; unsigned int old_i,new_i; int rc; rc = read_order(name, &order); if (rc < 0) { if (errno == ENOENT) rc = 0; return rc; } /* We've now got an array (in order->data) of the order. Squeeze out any instance of the entry we're deleting by shifting the remainder down. */ data = (uint16_t *)(order->data); for (old_i=0,new_i=0; old_i < order->data_size / sizeof(data[0]); old_i++) { if (data[old_i] != num) { if (new_i != old_i) data[new_i] = data[old_i]; new_i++; } } /* If nothing removed, no need to update the order variable */ if (new_i == old_i) goto all_done; /* *Order should have nothing when new_i == 0 */ if (new_i == 0) { efi_del_variable(EFI_GLOBAL_GUID, name); goto all_done; } order->data_size = sizeof(data[0]) * new_i; rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, order->data_size, order->attributes, 0644); all_done: free(order->data); free(order); return rc; } static int read_u16(const char *name) { efi_guid_t guid = EFI_GLOBAL_GUID; uint16_t *data = NULL; size_t data_size = 0; uint32_t attributes = 0; int rc; rc = efi_get_variable(guid, name, (uint8_t **)&data, &data_size, &attributes); if (rc < 0) return rc; if (data_size != 2) { if (data != NULL) free(data); errno = EINVAL; return -1; } rc = data[0]; free(data); return rc; } static int hex_could_be_lower_case(uint16_t num) { return ((((num & 0x000f) >> 0) > 9) || (((num & 0x00f0) >> 4) > 9) || (((num & 0x0f00) >> 8) > 9) || (((num & 0xf000) >> 12) > 9)); } static int delete_var(const char *prefix, uint16_t num) { int rc; char name[16]; list_t *pos, *n; var_entry_t *entry; snprintf(name, sizeof(name), "%s%04X", prefix, num); rc = efi_del_variable(EFI_GLOBAL_GUID, name); if (rc < 0) efi_error("Could not delete %s%04X", prefix, num); /* For backwards compatibility, try to delete abcdef entries as well */ if (rc < 0 && errno == ENOENT && hex_could_be_lower_case(num)) { snprintf(name, sizeof(name), "%s%04x", prefix, num); rc = efi_del_variable(EFI_GLOBAL_GUID, name); if (rc < 0 && errno != ENOENT) efi_error("Could not delete %s%04x", prefix, num); } if (rc < 0) return rc; else efi_error_clear(); snprintf(name, sizeof(name), "%sOrder", prefix); list_for_each_safe(pos, n, &entry_list) { entry = list_entry(pos, var_entry_t, list); if (entry->num == num) { rc = remove_from_order(name, num); if (rc < 0) { efi_error("remove_from_order(%s,%d) failed", name, num); return rc; } list_del(&(entry->list)); break; /* short-circuit since it was found */ } } return 0; } static void set_var_nums(const char *prefix, list_t *list) { list_t *pos; var_entry_t *var; int num=0, rc; char *name; int warn=0; size_t plen = strlen(prefix); char fmt[30]; fmt[0] = '\0'; strcat(fmt, prefix); strcat(fmt, "%04X-%*s"); list_for_each(pos, list) { var = list_entry(pos, var_entry_t, list); rc = sscanf(var->name, fmt, &num); if (rc == 1) { char *snum; var->num = num; name = var->name; /* shorter name */ snum = name + plen; if ((isalpha(snum[0]) && islower(snum[0])) || (isalpha(snum[1]) && islower(snum[1])) || (isalpha(snum[2]) && islower(snum[2])) || (isalpha(snum[3]) && islower(snum[3]))) { fprintf(stderr, "** Warning ** : %.8s is not UEFI Spec compliant (lowercase hex in name)\n", name); warn++; } } } if (warn) warningx("** Warning ** : please recreate these using efibootmgr to remove this warning."); } static void print_order(const char *name, uint16_t *order, int length) { int i; printf("%s: ", name); for (i=0; inum == b) return 1; } return 0; } static void print_error_arrow(char *buffer, off_t offset, char *fmt, ...) { va_list ap; size_t size; unsigned int i; va_start(ap, fmt); size = vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "%s\n", buffer); for (i = 0; i < size + 2; i++) fprintf(stderr, " "); for (i = 0; i < offset; i++) fprintf(stderr, " "); fprintf(stderr, "^\n"); } static int parse_order(const char *prefix, char *buffer, uint16_t **order, size_t *length) { uint16_t *data; size_t data_size; size_t len = strlen(buffer); intptr_t end = (intptr_t)buffer + len + 1; int num = 0; char *buf = buffer; while ((intptr_t)buf < end) { size_t comma = strcspn(buf, ","); if (comma == 0) { off_t offset = (intptr_t)buf - (intptr_t)buffer; print_error_arrow(buffer, offset, "Malformed %s order", prefix); exit(8); } else { num++; } buf += comma + 1; } if (num == 0) { *order = NULL; *length = 0; return 0; } data = calloc(num, sizeof (*data)); if (!data) return -1; data_size = num * sizeof (*data); int i = 0; buf = buffer; while ((intptr_t)buf < end) { unsigned long result = 0; size_t comma = strcspn(buf, ","); buf[comma] = '\0'; char *endptr = NULL; result = strtoul(buf, &endptr, 16); if ((result == ULONG_MAX && errno == ERANGE) || (endptr && *endptr != '\0')) { off_t offset = (intptr_t)endptr - (intptr_t)buffer; print_error_arrow(buffer, offset, "Invalid %s order", prefix); free(data); exit(8); } if (result > 0xffff) { off_t offset = (intptr_t)buf - (intptr_t)buffer; warnx("Invalid %s order entry value: %lX", prefix, result); print_error_arrow(buffer, offset, "Invalid %s order", prefix); free(data); exit(8); } /* make sure this is an existing entry */ if (!is_current_entry(result)) { off_t offset = (intptr_t)buf - (intptr_t)buffer; print_error_arrow(buffer, offset, "Invalid %s order entry value", prefix); warnx("entry %04lX does not exist", result); free(data); exit(8); } data[i++] = result; buf[comma] = ','; buf += comma + 1; } *order = data; *length = data_size; return num; } static int construct_order(const char *name, char *order, int keep, uint16_t **ret_data, size_t *ret_data_size) { var_entry_t bo; int rc; uint16_t *data = NULL; size_t data_size = 0; rc = parse_order(name, order, (uint16_t **)&data, &data_size); if (rc < 0 || data_size == 0) { if (data) /* this can't actually happen, but covscan believes */ free(data); return rc; } if (!keep) { *ret_data = data; *ret_data_size = data_size; return 0; } rc = efi_get_variable(EFI_GLOBAL_GUID, name, &bo.data, &bo.data_size, &bo.attributes); if (rc < 0) { *ret_data = data; *ret_data_size = data_size; return 0; } /* latest apple firmware sets high bit which appears invalid * to the linux kernel if we write it back so lets zero it out * if it is set since it would be invalid to set it anyway */ bo.attributes = bo.attributes & ~(1 << 31); size_t new_data_size = data_size + bo.data_size; uint16_t *new_data = calloc(1, new_data_size); if (!new_data) { if (data) free(data); return -1; } memcpy(new_data, data, data_size); memcpy(new_data + (data_size / sizeof (*new_data)), bo.data, bo.data_size); free(bo.data); free(data); int new_data_start = data_size / sizeof (uint16_t); int new_data_end = new_data_size / sizeof (uint16_t); int i; for (i = 0; i < new_data_start; i++) { int j; for (j = new_data_start; j < new_data_end; j++) { if (new_data[i] == new_data[j]) { memcpy(new_data + j, new_data + j + 1, sizeof (uint16_t) * (new_data_end-j+1)); new_data_end -= 1; break; } } } *ret_data = new_data; *ret_data_size = new_data_end * sizeof (uint16_t); return 0; } static int set_order(const char *order_name, const char *prefix, int keep_old_entries) { uint8_t *data = NULL; size_t data_size = 0; char *name; int rc; if (!opts.order) return 0; rc = construct_order(order_name, opts.order, keep_old_entries, (uint16_t **)&data, &data_size); if (rc < 0 || data_size == 0) { if (data) /* this can't happen, but clang analyzer believes */ free(data); return rc; } rc = asprintf(&name, "%sOrder", prefix); if (rc < 0) goto err; rc = efi_set_variable(EFI_GLOBAL_GUID, name, data, data_size, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0644); free(name); err: free(data); return rc; } #define ev_bits(val, mask, shift) \ (((val) & ((mask) << (shift))) >> (shift)) static inline char * ucs2_to_utf8(const uint16_t * const chars, ssize_t limit) { ssize_t i, j; char *ret; ret = alloca(limit * 6 + 1); if (!ret) return NULL; memset(ret, 0, limit * 6 +1); for (i=0, j=0; chars[i] && i < (limit >= 0 ? limit : i+1); i++,j++) { if (chars[i] <= 0x7f) { ret[j] = chars[i]; } else if (chars[i] > 0x7f && chars[i] <= 0x7ff) { ret[j++] = 0xc0 | ev_bits(chars[i], 0x1f, 6); ret[j] = 0x80 | ev_bits(chars[i], 0x3f, 0); } else if (chars[i] > 0x7ff) { ret[j++] = 0xe0 | ev_bits(chars[i], 0xf, 12); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 6); ret[j] = 0x80| ev_bits(chars[i], 0x3f, 0); } } ret[j] = '\0'; return strdup(ret); } static void show_vars(const char *prefix) { list_t *pos; var_entry_t *boot; const unsigned char *description; efi_load_option *load_option; efidp dp = NULL; unsigned char *optional_data = NULL; size_t optional_data_len=0; list_for_each(pos, &entry_list) { boot = list_entry(pos, var_entry_t, list); load_option = (efi_load_option *)boot->data; description = efi_loadopt_desc(load_option, boot->data_size); if (boot->name) printf("%s", boot->name); else printf("%s%04X", prefix, boot->num); printf("%c ", (efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE) ? '*' : ' '); printf("%s", description); if (opts.verbose) { char *text_path = NULL; size_t text_path_len = 0; uint16_t pathlen; ssize_t rc; pathlen = efi_loadopt_pathlen(load_option, boot->data_size); dp = efi_loadopt_path(load_option, boot->data_size); rc = efidp_format_device_path(text_path, text_path_len, dp, pathlen); if (rc < 0) error(18, "Could not parse device path"); rc += 1; text_path_len = rc; text_path = calloc(1, rc); if (!text_path) error(19, "Could not parse device path"); rc = efidp_format_device_path(text_path, text_path_len, dp, pathlen); if (rc < 0) error(20, "Could not parse device path"); printf("\t%s", text_path); free(text_path); /* Print optional data */ rc = efi_loadopt_optional_data(load_option, boot->data_size, &optional_data, &optional_data_len); if (rc < 0) error(21, "Could not parse optional data"); if (opts.unicode) { text_path = ucs2_to_utf8((uint16_t*)optional_data, optional_data_len/2); } else { rc = unparse_raw_text(NULL, 0, optional_data, optional_data_len); if (rc < 0) error(22, "Could not parse optional data"); rc += 1; text_path_len = rc; text_path = calloc(1, rc); if (!text_path) error(23, "Could not parse optional data"); rc = unparse_raw_text(text_path, text_path_len, optional_data, optional_data_len); if (rc < 0) error(24, "Could not parse device path"); } printf("%s", text_path); free(text_path); } printf("\n"); fflush(stdout); } } static void show_order(const char *name) { int rc; var_entry_t *order = NULL; uint16_t *data; rc = read_order(name, &order); cond_warning(opts.verbose >= 2 && rc < 0, "Could not read variable '%s'", name); if (rc < 0) { if (errno == ENOENT) { if (!strcmp(name, "BootOrder")) printf("No BootOrder is set; firmware will attempt recovery\n"); else printf("No %s is set\n", name); } else perror("show_order()"); return; } /* We've now got an array (in order->data) of the order. First add * our entry, then copy the old array. */ data = (uint16_t *)order->data; if (order->data_size) { print_order(name, data, order->data_size / sizeof(uint16_t)); free(order->data); } free(order); } static int set_active_state(const char *prefix) { list_t *pos; var_entry_t *entry; efi_load_option *load_option; int rc; list_for_each(pos, &entry_list) { entry = list_entry(pos, var_entry_t, list); load_option = (efi_load_option *)entry->data; if (entry->num != opts.num) continue; if (opts.active == 1) { if (efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE) { return 0; } else { efi_loadopt_attr_set(load_option, LOAD_OPTION_ACTIVE); rc = efi_set_variable(entry->guid, entry->name, entry->data, entry->data_size, entry->attributes, 0644); if (rc < 0) { char *guid = NULL; int err = errno; efi_guid_to_str(&entry->guid, &guid); errno = err; efi_error( "efi_set_variable(%s,%s,...)", guid, entry->name); } return rc; } } else if (opts.active == 0) { if (!(efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE)) { return 0; } else { efi_loadopt_attr_clear(load_option, LOAD_OPTION_ACTIVE); rc = efi_set_variable(entry->guid, entry->name, entry->data, entry->data_size, entry->attributes, 0644); if (rc < 0) { char *guid = NULL; int err = errno; efi_guid_to_str(&entry->guid, &guid); errno = err; efi_error("efi_set_variable(%s,%s,...)", guid, entry->name); } return rc; } } } /* if we reach here then the number supplied was not found */ warnx("%s entry %x not found", prefix, opts.num); errno = ENOENT; return -1; } static int get_mirror(int which, int *below4g, int *above4g, int *mirrorstatus) { int rc; uint8_t *data; ADDRESS_RANGE_MIRROR_VARIABLE_DATA *abm; size_t data_size; uint32_t attributes; char *name; if (which) name = ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST; else name = ADDRESS_RANGE_MIRROR_VARIABLE_CURRENT; rc = efi_get_variable(ADDRESS_RANGE_MIRROR_VARIABLE_GUID, name, &data, &data_size, &attributes); if (rc == 0) { abm = (ADDRESS_RANGE_MIRROR_VARIABLE_DATA *)data; if (!which && abm->mirror_version != MIRROR_VERSION) { rc = 2; } *below4g = abm->mirror_memory_below_4gb; *above4g = abm->mirror_amount_above_4gb; *mirrorstatus = abm->mirror_status; free(data); } else { cond_warning(opts.verbose >= 2, "Could not read variable '%s'", name); errno = 0; } return rc; } static int set_mirror(int below4g, int above4g) { int s, status, rc; uint8_t *data; ADDRESS_RANGE_MIRROR_VARIABLE_DATA abm; size_t data_size; uint32_t attributes; int oldbelow4g, oldabove4g; if ((s = get_mirror(0, &oldbelow4g, &oldabove4g, &status)) != 0) { if (s == 2) warningx("** Warning ** : unrecognised version for memory mirror i/f"); else warningx("** Warning ** : platform does not support memory mirror"); return s; } below4g = opts.set_mirror_lo ? below4g : oldbelow4g; above4g = opts.set_mirror_hi ? above4g : oldabove4g; if (oldbelow4g == below4g && oldabove4g == above4g) return 0; data = (uint8_t *)&abm; data_size = sizeof (abm); attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; abm.mirror_version = MIRROR_VERSION; abm.mirror_amount_above_4gb = above4g; abm.mirror_memory_below_4gb = below4g; abm.mirror_status = 0; rc = efi_set_variable(ADDRESS_RANGE_MIRROR_VARIABLE_GUID, ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST, data, data_size, attributes, 0644); if (rc < 0) efi_error("efi_set_variable() failed"); return rc; } static void show_mirror(void) { int status; int below4g = 0, above4g = 0; int rbelow4g = 0, rabove4g = 0; if (get_mirror(0, &below4g, &above4g, &status) == 0) { if (status == 0) { printf("MirroredPercentageAbove4G: %d.%.2d\n", above4g/100, above4g%100); printf("MirrorMemoryBelow4GB: %s\n", below4g ? "true" : "false"); } else { printf("MirrorStatus: "); switch (status) { case 1: printf("Platform does not support address range mirror\n"); break; case 2: printf("Invalid version number\n"); break; case 3: printf("MirroredMemoryAbove4GB > 50.00%%\n"); break; case 4: printf("DIMM configuration does not allow mirror\n"); break; case 5: printf("OEM specific method\n"); break; default: printf("%u\n", status); break; } printf("DesiredMirroredPercentageAbove4G: %d.%.2d\n", above4g/100, above4g%100); printf("DesiredMirrorMemoryBelow4GB: %s\n", below4g ? "true" : "false"); } } if ((get_mirror(1, &rbelow4g, &rabove4g, &status) == 0) && (above4g != rabove4g || below4g != rbelow4g)) { printf("RequestMirroredPercentageAbove4G: %d.%.2d\n", rabove4g/100, rabove4g%100); printf("RequestMirrorMemoryBelow4GB: %s\n", rbelow4g ? "true" : "false"); } } static void usage() { printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION); printf("usage: efibootmgr [options]\n"); printf("\t-a | --active sets bootnum active\n"); printf("\t-A | --inactive sets bootnum inactive\n"); printf("\t-b | --bootnum XXXX modify BootXXXX (hex)\n"); printf("\t-B | --delete-bootnum delete bootnum\n"); printf("\t-c | --create create new variable bootnum and add to bootorder\n"); printf("\t-C | --create-only create new variable bootnum and do not add to bootorder\n"); printf("\t-D | --remove-dups remove duplicate values from BootOrder\n"); printf("\t-d | --disk disk (defaults to /dev/sda) containing loader\n"); printf("\t-r | --driver Operate on Driver variables, not Boot Variables.\n"); printf("\t-e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess\n"); printf("\t-E | --device num EDD 1.0 device number (defaults to 0x80)\n"); printf("\t-g | --gpt force disk with invalid PMBR to be treated as GPT\n"); printf("\t-i | --iface name create a netboot entry for the named interface\n"); #if 0 printf("\t --ip-addr , set local and remote IP addresses\n"); printf("\t --ip-gateway set the network gateway\n"); printf("\t --ip-netmask set the netmask or prefix length\n"); printf("\t --ip-proto TCP|UDP set the IP protocol to be used\n"); printf("\t --ip-port , set local and remote IP ports\n"); printf("\t --ip-origin { {dhcp|static} | { static|stateless|stateful} }\n"); #endif printf("\t-l | --loader name (defaults to \""DEFAULT_LOADER"\")\n"); printf("\t-L | --label label Boot manager display label (defaults to \"Linux\")\n"); printf("\t-m | --mirror-below-4G t|f mirror memory below 4GB\n"); printf("\t-M | --mirror-above-4G X percentage memory to mirror above 4GB\n"); printf("\t-n | --bootnext XXXX set BootNext to XXXX (hex)\n"); printf("\t-N | --delete-bootnext delete BootNext\n"); printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex)\n"); printf("\t-O | --delete-bootorder delete BootOrder\n"); printf("\t-p | --part part partition containing loader (defaults to 1 on partitioned devices)\n"); printf("\t-q | --quiet be quiet\n"); printf("\t-t | --timeout seconds set boot manager timeout waiting for user input.\n"); printf("\t-T | --delete-timeout delete Timeout.\n"); printf("\t-u | --unicode | --UCS-2 handle extra args as UCS-2 (default is ASCII)\n"); printf("\t-v | --verbose print additional information\n"); printf("\t-V | --version return version and exit\n"); printf("\t-w | --write-signature write unique sig to MBR if needed\n"); printf("\t-y | --sysprep Operate on SysPrep variables, not Boot Variables.\n"); printf("\t-@ | --append-binary-args file append extra args from file (use \"-\" for stdin)\n"); printf("\t-h | --help show help/usage\n"); } static void set_default_opts() { memset(&opts, 0, sizeof(opts)); opts.num = -1; /* auto-detect */ opts.bootnext = -1; /* Don't set it */ opts.active = -1; /* Don't set it */ opts.timeout = -1; /* Don't set it */ opts.edd10_devicenum = 0x80; opts.loader = DEFAULT_LOADER; opts.label = (unsigned char *)"Linux"; opts.disk = "/dev/sda"; opts.part = -1; } static void parse_opts(int argc, char **argv) { int c, rc; unsigned int num; int snum; float fnum; int option_index = 0; while (1) { static struct option long_options[] = /* name, has_arg, flag, val */ { {"active", no_argument, 0, 'a'}, {"inactive", no_argument, 0, 'A'}, {"bootnum", required_argument, 0, 'b'}, {"delete-bootnum", no_argument, 0, 'B'}, {"create", no_argument, 0, 'c'}, {"create-only", no_argument, 0, 'C'}, {"remove-dups", no_argument, 0, 'D'}, {"disk", required_argument, 0, 'd'}, {"iface", required_argument, 0, 'i'}, {"edd-device", required_argument, 0, 'E'}, {"edd30", required_argument, 0, 'e'}, {"gpt", no_argument, 0, 'g'}, {"keep", no_argument, 0, 'k'}, {"loader", required_argument, 0, 'l'}, {"label", required_argument, 0, 'L'}, {"mirror-below-4G", required_argument, 0, 'm'}, {"mirror-above-4G", required_argument, 0, 'M'}, {"bootnext", required_argument, 0, 'n'}, {"delete-bootnext", no_argument, 0, 'N'}, {"bootorder", required_argument, 0, 'o'}, {"delete-bootorder", no_argument, 0, 'O'}, {"part", required_argument, 0, 'p'}, {"quiet", no_argument, 0, 'q'}, {"timeout", required_argument, 0, 't'}, {"delete-timeout", no_argument, 0, 'T'}, {"unicode", no_argument, 0, 'u'}, {"UCS-2", no_argument, 0, 'u'}, {"verbose", optional_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"write-signature", no_argument, 0, 'w'}, {"append-binary-args", required_argument, 0, '@'}, {"driver", no_argument, 0, 'r'}, {"sysprep", no_argument, 0, 'y'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "AaBb:cCDd:e:E:gH:i:l:L:M:m:n:No:Op:qt:TuU:v::Vw" "@:hry", long_options, &option_index); if (c == -1) break; switch (c) { case '@': opts.extra_opts_file = optarg; break; case 'a': opts.active = 1; break; case 'A': opts.active = 0; break; case 'B': opts.delete = 1; break; case 'b': { char *endptr = NULL; unsigned long result; result = strtoul(optarg, &endptr, 16); if ((result == ULONG_MAX && errno == ERANGE) || (endptr && *endptr != '\0')) { off_t offset = (intptr_t)endptr - (intptr_t)optarg; print_error_arrow(optarg, offset, "Invalid bootnum value"); conditional_error_reporter(opts.verbose >= 2, 1); exit(28); } if (result > 0xffff) errorx(29, "Invalid bootnum value: %lX\n", result); opts.num = result; break; } case 'c': opts.create = 1; break; case 'C': opts.create = 1; opts.no_order = 1; break; case 'D': opts.deduplicate = 1; break; case 'd': opts.disk = optarg; break; case 'e': rc = sscanf(optarg, "%d", &snum); if (rc == 1) opts.edd_version = snum; else errorx(30, "invalid numeric value %s\n", optarg); if (snum == -1) snum = 0; if (snum != 0 && snum != 1 && snum != 3) errorx(31, "invalid EDD version %d\n", snum); break; case 'E': rc = sscanf(optarg, "%x", &num); if (rc == 1) opts.edd10_devicenum = num; else errorx(32, "invalid hex value %s\n", optarg); break; case 'g': opts.forcegpt = 1; break; case 'h': usage(); exit(0); break; case 'i': opts.iface = optarg; opts.ip_version = EFIBOOTMGR_IPV4; opts.ip_addr_origin = EFIBOOTMGR_IPV4_ORIGIN_DHCP; break; case 'k': opts.keep_old_entries = 1; break; case 'l': opts.loader = optarg; break; case 'L': opts.label = (unsigned char *)optarg; break; case 'm': opts.set_mirror_lo = 1; switch (optarg[0]) { case '1': case 'y': case 't': opts.below4g = 1; break; case '0': case 'n': case 'f': opts.below4g = 0; break; default: errorx(33, "invalid boolean value %s\n", optarg); } break; case 'M': opts.set_mirror_hi = 1; rc = sscanf(optarg, "%f", &fnum); if (rc == 1 && fnum <= 50 && fnum >= 0) /* percent to basis points */ opts.above4g = fnum * 100; else errorx(34, "invalid numeric value %s\n", optarg); break; case 'N': opts.delete_bootnext = 1; break; case 'n': { char *endptr = NULL; unsigned long result; result = strtoul(optarg, &endptr, 16); if ((result == ULONG_MAX && errno == ERANGE) || (endptr && *endptr != '\0')) { off_t offset = (intptr_t)endptr - (intptr_t)optarg; print_error_arrow(optarg, offset, "Invalid BootNext value"); conditional_error_reporter(opts.verbose >= 2, 1); exit(35); } if (result > 0xffff) errorx(36, "Invalid BootNext value: %lX\n", result); opts.bootnext = result; break; } case 'o': opts.order = optarg; break; case 'O': opts.delete_order = 1; break; case 'p': rc = sscanf(optarg, "%u", &num); if (rc == 1) opts.part = num; else errorx(37, "invalid numeric value %s\n", optarg); break; case 'q': opts.quiet = 1; break; case 'r': opts.driver = 1; break; case 't': rc = sscanf(optarg, "%u", &num); if (rc == 1) { opts.timeout = num; opts.set_timeout = 1; } else { errorx(38, "invalid numeric value %s\n", optarg); } break; case 'T': opts.delete_timeout = 1; break; case 'u': opts.unicode = 1; break; case 'v': opts.verbose += 1; if (optarg) { if (!strcmp(optarg, "v")) opts.verbose = 2; if (!strcmp(optarg, "vv")) opts.verbose = 3; rc = sscanf(optarg, "%u", &num); if (rc == 1) opts.verbose = num; else errorx(39, "invalid numeric value %s\n", optarg); } /* XXX efivar-36 accidentally doesn't have a public * header for this */ extern int efi_set_verbose(int verbosity, FILE *errlog); efi_set_verbose(opts.verbose - 2, stderr); break; case 'V': opts.showversion = 1; break; case 'w': opts.write_signature = 1; break; case 'y': opts.sysprep = 1; break; default: usage(); exit(1); } } if (optind < argc) { opts.argc = argc; opts.argv = argv; opts.optind = optind; } } int main(int argc, char **argv) { char **names = NULL; var_entry_t *new_entry = NULL; int num; int ret = 0; ebm_mode mode = boot; char *prefices[] = { "Boot", "Driver", "SysPrep", }; char *order_name[] = { "BootOrder", "DriverOrder", "SysPrepOrder" }; set_default_opts(); parse_opts(argc, argv); if (opts.showversion) { printf("version %s\n", EFIBOOTMGR_VERSION); return 0; } verbose = opts.verbose; if (opts.sysprep && opts.driver) errx(25, "--sysprep and --driver may not be used together."); if (opts.sysprep || opts.driver) { if (opts.bootnext >= 0 || opts.delete_bootnext) errx(26, "%s mode does not support BootNext options.", opts.sysprep ? "--sysprep": "--driver"); if (opts.timeout >= 0 || opts.delete_timeout) errx(27, "%s mode does not support timeout options.", opts.sysprep ? "--sysprep": "--driver"); if (opts.sysprep) mode = sysprep; if (opts.driver) mode = driver; } if (!efi_variables_supported()) errorx(2, "EFI variables are not supported on this system."); read_var_names(prefices[mode], &names); read_vars(names, &entry_list); set_var_nums(prefices[mode], &entry_list); if (opts.delete) { if (opts.num == -1) errorx(3, "You must specify an entry to delete " "(see the -b option)."); else { ret = delete_var(prefices[mode], opts.num); if (ret < 0) error(15, "Could not delete variable"); } } if (opts.active >= 0) { if (opts.num == -1) { errorx(4, "You must specify a entry to activate (see the -b option"); } else { ret = set_active_state(prefices[mode]); if (ret < 0) error(16, "%s", "Could not set active state for %s%04X", prefices[mode], opts.num); } } if (opts.create) { warn_duplicate_name(&entry_list); new_entry = make_var(prefices[mode], &entry_list); if (!new_entry) error(5, "Could not prepare %s variable", prefices[mode]); /* Put this boot var in the right Order variable */ if (new_entry && !opts.no_order) { ret = add_to_order(order_name[mode], new_entry->num); if (ret < 0) error(6, "Could not add entry to %s", order_name[mode]); } } if (opts.delete_order) { ret = efi_del_variable(EFI_GLOBAL_GUID, order_name[mode]); if (ret < 0 && errno != ENOENT) error(7, "Could not remove entry from %s", order_name[mode]); } if (opts.order) { ret = set_order(order_name[mode], prefices[mode], opts.keep_old_entries); if (ret < 0) error(8, "Could not set %s", order_name[mode]); } if (opts.deduplicate) { ret = remove_dupes_from_order(order_name[mode]); if (ret) error(9, "Could not set %s", order_name[mode]); } if (opts.delete_bootnext) { if (!is_current_entry(opts.delete_bootnext)) errorx(17, "Boot entry %04X does not exist", opts.delete_bootnext); ret = efi_del_variable(EFI_GLOBAL_GUID, "BootNext"); if (ret < 0) error(10, "Could not delete BootNext"); } if (opts.delete_timeout) { ret = efi_del_variable(EFI_GLOBAL_GUID, "Timeout"); if (ret < 0) error(11, "Could not delete Timeout"); } if (opts.bootnext >= 0) { if (!is_current_entry(opts.bootnext & 0xFFFF)) errorx(12, "Boot entry %X does not exist", opts.bootnext); ret = set_u16("BootNext", opts.bootnext & 0xFFFF); if (ret < 0) error(13, "Could not set BootNext"); } if (opts.set_timeout) { ret = set_u16("Timeout", opts.timeout); if (ret < 0) error(14, "Could not set Timeout"); } if (opts.set_mirror_lo || opts.set_mirror_hi) { ret=set_mirror(opts.below4g, opts.above4g); } if (!opts.quiet && ret == 0) { switch (mode) { case boot: num = read_u16("BootNext"); cond_warning(opts.verbose >= 2 && num < 0, "Could not read variable 'BootNext'"); if (num >= 0) printf("BootNext: %04X\n", num); num = read_u16("BootCurrent"); cond_warning(opts.verbose >= 2 && num < 0, "Could not read variable 'BootCurrent'"); if (num >= 0) printf("BootCurrent: %04X\n", num); num = read_u16("Timeout"); cond_warning(opts.verbose >= 2 && num < 0, "Could not read variable 'Timeout'"); if (num >= 0) printf("Timeout: %u seconds\n", num); show_order(order_name[mode]); show_vars(prefices[mode]); show_mirror(); break; case driver: case sysprep: show_order(order_name[mode]); show_vars(prefices[mode]); break; } } free_vars(&entry_list); free_array(names); if (ret) return 1; return 0; } efibootmgr-17/src/efibootnext.c000066400000000000000000000140051330730303200167130ustar00rootroot00000000000000/* * efibootnext - Attempt to set a BootNext variable from existing boot * options. * * Copyright 2015 Red Hat, Inc. * Author: Peter Jones * * 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 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this library; if not, see . * */ #include "fix_coverity.h" #include #include #include #include #include typedef enum { notnot, // just a regular match sense ignore, // ignore this match - only consider its children not, // ! and, // -a or, // -o } match_sense; struct matcher { int sense; struct matcher *matchers; char *bootnum; char *disk; char *edd; char *edd_devnum; char *loader; char *label; int gpt; int in_boot_order; }; int main(int argc, char *argv[]) { int err_if_not_found = 1; int err_if_set_fails = 1; int which = 0; char *sorter = NULL; struct matcher *matchers; struct matcher *matcher; poptContext optCon; matcher = matchers = calloc(1, sizeof(struct matcher)); struct poptOption matchopts[] = { /* options to specify match criteria */ {"bootnum", 'n', POPT_ARG_STRING, matcher->bootnum, NULL, "boot entry number (hex)", "<####>" }, {"disk", 'd', POPT_ARG_STRING, matcher->disk, NULL, "disk containing loader", "" }, /* keep edd and device together despite alphabetism */ {"edd", 'e', POPT_ARG_STRING, matcher->edd, NULL, "EDD version", "[1|3|[any]]" }, {"device", 'E', POPT_ARG_STRING, matcher->edd_devnum, NULL, "EDD 1.0 device number (hex)", "[##|[80]]", }, /* keep gpt and mbr together despite alphabetism */ {"gpt", 'g', POPT_ARG_VAL, &matcher->gpt, 1, "only match GPT partitioned disks", }, {"mbr", 'm', POPT_ARG_VAL, &matcher->gpt, 2, "only match MBR partitioned disks", }, {"loader", 'l', POPT_ARG_STRING, matcher->loader, NULL, "loader path", "", }, {"label", 'L', POPT_ARG_STRING, matcher->label, NULL, "boot entry label", "