pax_global_header00006660000000000000000000000064133152210630014506gustar00rootroot0000000000000052 comment=6101b6b304da06644bd7a90444f729d0fc44940e fwupdate-12/000077500000000000000000000000001331522106300131115ustar00rootroot00000000000000fwupdate-12/.gitignore000066400000000000000000000002171331522106300151010ustar00rootroot00000000000000*.efi *.efi.unsigned *.efi.debug *.efi.build-id *.so *.o *.a *.E *.pc *.spec *.tar.* .*.sw? linux/include/fwup-version.h cov-int scan-results/ fwupdate-12/.travis.yml000066400000000000000000000013251331522106300152230ustar00rootroot00000000000000language: 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 fwupdate" ; fi fwupdate-12/COPYING000066400000000000000000000430051331522106300141460ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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, see . 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. fwupdate-12/Make.coverity000066400000000000000000000024551331522106300155620ustar00rootroot00000000000000COV_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 fwupdate-12/Make.defaults000066400000000000000000000051511331522106300155210ustar00rootroot00000000000000include $(TOP)/Make.version ifneq ($(origin RELEASE),undefined) DASHRELEASE ?= -$(RELEASE) else DASHRELEASE ?= endif NAME = fwupdate COMMIT_ID = $(shell git log -1 --pretty=%H 2>/dev/null || echo master) INSTALL ?= install MAKE ?= make PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy READELF = eu-readelf XGETTEXT = xgettext ABIDIFF := abidiff ABIDW := abidw prefix ?= /usr/ prefix := $(abspath $(prefix)) exec_prefix ?= $(prefix) ARCH = $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,) ifeq ($(ARCH),x86_64) LIBDIR ?= $(exec_prefix)/lib64 ifeq "$(wildcard $(LIBDIR) )" "" LIBDIR = $(exec_prefix)/lib/x86_64-linux-gnu endif endif ifeq ($(ARCH),ia32) LIBDIR ?= $(exec_prefix)/lib endif ifeq ($(ARCH),aarch64) LIBDIR ?= $(exec_prefix)/lib64 endif ifeq ($(ARCH),arm) LIBDIR ?= $(exec_prefix)/lib endif LIBDIR ?= unknown ifeq ($(LIBDIR),unknown) $(error Architecture $(ARCH) is not a supported build target.) endif ifneq ($(origin RPMARCH),undefined) DOTARCH ?= .$(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,i686,) else DOTARCH ?= .$(RPMARCH) endif GNUEFIDIR ?= $(LIBDIR)/gnuefi ifeq "$(wildcard $(GNUEFIDIR) )" "" GNUEFIDIR = $(prefix)/lib endif libdir ?= $(LIBDIR) pcdir ?= $(libdir)/pkgconfig mandir ?= $(prefix)/share/man includedir ?= $(prefix)/include bindir ?= $(exec_prefix)/bin datadir ?= $(prefix)/share localedir ?= $(datadir)/locale libexecdir ?= $(exec_prefix)/libexec ifeq "$(wildcard $(libexecdir) )" "" libexecdir = $(exec_prefix)/lib endif libdatadir ?= $(exec_prefix)/lib sharedstatedir ?= /var/lib EFIDIR ?= $(shell x=$$(which --skip-alias --skip-functions git 2>/dev/null) ; [ -n "$$x" ] && git config --get fwupdate.efidir) ifeq ($(EFIDIR),) ifneq ("$(wildcard /etc/os-release)","") RELEASE := /etc/os-release else ifneq ("$(wildcard /usr/lib/os-release)","") RELEASE := /usr/lib/os-release endif ifneq ($(RELEASE),) EFIDIR = $(shell sed '/^ID=/!d; s/ID=//' $(RELEASE)) endif ifeq ($(EFIDIR),) EFIDIR_ERROR = $(error EFIDIR or .gitconfig fwupdate.efidir must be set to this distro's reserved EFI System Partition subdirectory name) endif endif ESPMOUNTPOINT ?= $(shell x=$$(which --skip-alias --skip-functions git 2>/dev/null) ; [ -n "$$x" ] && git config --get fwupdate.espmountdir) ifeq ($(ESPMOUNTPOINT),) ESPMOUNTPOINT = "/boot/efi" endif DEBUGINFO ?= $(exec_prefix)/lib/debug DEBUGSOURCE ?= $(prefix)/src/debug TARGETDIR ?= $(ESPMOUNTPOINT)/EFI/$(EFIDIR) .PHONY: check_efidir_error check_efidir_error : ; $(EFIDIR_ERROR) $(info Building with EFIDIR as $(EFIDIR)) fwupdate-12/Make.rules000066400000000000000000000005011331522106300150360ustar00rootroot00000000000000 define get-config $(shell git config --local --get "fwupdate.$(1)") endef %.abixml : %.so $(ABIDW) --headers-dir $(TOP)/linux/include/ --out-file $@ $^ @sed -i -s 's,$(TOPDIR)/,,g' $@ %.abicheck : %.so $(ABIDIFF) \ --suppr abignore \ --headers-dir2 $(TOP)/linux/include/ \ $(patsubst %.so,%.abixml,$<) \ $< fwupdate-12/Make.scan-build000066400000000000000000000011011331522106300157220ustar00rootroot00000000000000SCAN_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 fwupdate-12/Make.version000066400000000000000000000000131331522106300153670ustar00rootroot00000000000000VERSION=12 fwupdate-12/Makefile000066400000000000000000000042041331522106300145510ustar00rootroot00000000000000default : all ifneq ($(origin TOPDIR),undefined) TOP := $(abspath $(TOPDIR)) else TOP = $(abspath $(shell pwd)) endif include $(TOP)/Make.defaults include $(TOP)/Make.rules include $(TOP)/Make.coverity include $(TOP)/Make.scan-build SUBDIRS ?= efi linux docs include all abicheck clean install : | check_efidir_error @set -e ; for x in $(SUBDIRS) ; do \ if [ ! -d $${x} ]; then \ install -m 0755 -d $${x} ; \ fi ; \ $(MAKE) DESTDIR=$(DESTDIR) TOPDIR=$(TOP) VERSION=$(VERSION) \ LIBDIR=$(LIBDIR) bindir=$(bindir) mandir=$(mandir) \ -C $$x/ -f $(TOP)/$$x/Makefile $@ ; \ done abiupdate : $(MAKE) clean all $(MAKE) -C linux TOPDIR=$(TOP) abiclean abiupdate GITTAG = $(shell bash -c "echo $$(($(VERSION) + 1))") fwupdate.spec : | Makefile fwupdate.spec : $(TOP)/fwupdate.spec.in @sed -e "s,@@VERSION@@,$(VERSION),g" $< > $@ test-archive: abicheck fwupdate.spec @rm -rf /tmp/fwupdate-$(GITTAG) /tmp/fwupdate-$(GITTAG)-tmp @mkdir -p /tmp/fwupdate-$(GITTAG)-tmp @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/fwupdate-$(GITTAG)-tmp/ ; tar x ) @git diff | ( cd /tmp/fwupdate-$(GITTAG)-tmp/ ; patch -s -p1 -b -z .gitdiff ) @mv /tmp/fwupdate-$(GITTAG)-tmp/ /tmp/fwupdate-$(GITTAG)/ @cp fwupdate.spec /tmp/fwupdate-$(GITTAG)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/fwupdate-$(GITTAG).tar.bz2 fwupdate-$(GITTAG) @rm -rf /tmp/fwupdate-$(GITTAG) @echo "The archive is in fwupdate-$(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: abicheck abiupdate bumpver tag fwupdate.spec @rm -rf /tmp/fwupdate-$(GITTAG) /tmp/fwupdate-$(GITTAG)-tmp @mkdir -p /tmp/fwupdate-$(GITTAG)-tmp @git archive --format=tar $(GITTAG) | ( cd /tmp/fwupdate-$(GITTAG)-tmp/ ; tar x ) @mv /tmp/fwupdate-$(GITTAG)-tmp/ /tmp/fwupdate-$(GITTAG)/ @cp fwupdate.spec /tmp/fwupdate-$(GITTAG)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/fwupdate-$(GITTAG).tar.bz2 fwupdate-$(GITTAG) @rm -rf /tmp/fwupdate-$(GITTAG) @echo "The archive is in fwupdate-$(GITTAG).tar.bz2" .PHONY: $(SUBDIRS) abiupdate fwupdate-12/README.md000066400000000000000000000061651331522106300144000ustar00rootroot00000000000000UEFI firmware capsules for Linux -------- This project provides the pieces needed to flash an industry standard UEFI capsule in a Linux OS. It also aims to be compatible with some implementation decisions that were made in Windows. The following binaries are produced: * `libfwup` library providing APIs to do UEFI updates for other applications * `fwupdate` command line tool * `fwup.efi` EFI application used for flashing the update from EFI. ## Normal flow UEFI capsule updates are _not_ actually flashed within Linux. They're staged for update to be installed on the next boot. 1. A higher level tool such as [fwupd](https://github.com/hughsie/fwupd) will consume a _.CAB_ file. 2. That tool will use libfwup from this project to stage the updates on the system. 3. `libfwup` will copy the payload to the EFI system partition. 4. `libfwup` will create EFI NVRAM entries pointing to the correct payload on the EFI system partition. 5. `libfwup` will create a new EFI Boot entry to launch the firmware updating EFI application. 6. `libfwup` will set the `BootNext` variable to run that application on next boot. 7. The user will reboot the system. 8. The `fwup.efi` application will examine EFI NVRAM entries to find capsules previously staged. 9. The `fwup.efi` applciation will call the BIOS `UpdateCapsule()` method to flash the capsules. 10. The BIOS will flash the capsules and then reboot back into the OS. ## Usage UEFI capsule updates are typically distributed by services such as [LVFS](https://fwupd.org) in _.CAB_ format. The command line tool provided by this project works directly on the payload stored in the *.CAB*. Most users should apply UEFI capsule updates with a higher level tool such as [fwupd](https://github.com/hughsie/fwupd) that uses the library `libfwup` that is provided by this project. ## Dependencies The following dependencies are needed to compile: * libpopt * efivar (>=33) * gnu-efi (>= 3.0.2) * elfutils Optionally if libsmbios is present some additional features on Dell systems can be enabled as well. ## Compiling Optionally set the EFI system partition mount point. If not configured it will default to `/boot/efi` ``` # git config fwupdate.espmountdir $DIRECTORY ``` Set the EFI subdirectory directory that EFI binaries are installed into. ``` # git config fwupdate.efidir $DIRECTORY ``` This usually refers to the OS distributor. If not configured it will default to the value defined for `ID` in `/etc/os-release` or `/usr/lib/os-release`. Run the build command ``` # make ``` Run the install command ``` # make install ``` ## Other notes Some distributions don't use the same paths for the dependencies as in Make.defaults. For example on Debian and Ubuntu you need to override `GNUEFIDIR` to the correct path. ## Command line Checking if UEFI capsule updates are supported: ``` # fwupdate --supported ``` Checking which ESRT GUIDs are on the system: ``` # fwupdate --list ``` Display details of all ESRT entries: ``` # fwupdate --info ``` Applying a payload: ``` # fwupdate --apply= ``` Enable firmware updates on supported Dell systems (if compiled with libsmbios): ``` # fwupdate --enable ``` fwupdate-12/TODO000066400000000000000000000002051331522106300135760ustar00rootroot00000000000000Things that need to be done before 1.0: 1) security audit for efi/fwupdate.c with secure boot violation in mind 2) debug Canoe Pass fwupdate-12/docs/000077500000000000000000000000001331522106300140415ustar00rootroot00000000000000fwupdate-12/docs/Makefile000066400000000000000000000022771331522106300155110ustar00rootroot00000000000000ifneq ($(origin TOPDIR),undefined) TOP := $(abspath $(TOPDIR)) else TOP := $(abspath ..) endif VPATH = $(TOP)/docs include $(TOP)/Make.defaults MAN1TARGETS = fwupdate.1 MAN3TARGETS = libfwup.h.3 \ libfwup.3 \ fwup_supported.3 \ fwup_resource_iter_next.3 \ fwup_resource_iter_create.3 \ fwup_resource_iter_destroy.3 \ fwup_resource_free.3 \ fwup_set_up_update.3 \ fwup_clear_status.3 \ fwup_set_guid.3 \ fwup_set_guid_forced.3 \ fwup_get_guid.3 \ fwup_get_fw_version.3 \ fwup_get_lowest_supported_version.3 \ fwup_get_last_attempt_info.3 \ fwup_get_fw_type.3 \ fwup_get_ux_capsule_info.3 MANSECTIONS = 1 3 all clean test abidw abicheck : mandirs : $(INSTALL) -d -m 755 $(DESTDIR)$(mandir)/man1/ $(INSTALL) -d -m 755 $(DESTDIR)$(mandir)/man3/ install : | mandirs install : | $(foreach x, $(MAN1TARGETS), $(DESTDIR)$(mandir)/man1/$(x)) install : | $(foreach x, $(MAN3TARGETS), $(DESTDIR)$(mandir)/man3/$(x)) $(DESTDIR)$(mandir)/man1/%.1 : %.1 | mandirs $(INSTALL) -m 644 $< $@ $(DESTDIR)$(mandir)/man3/%.3 : %.3 | mandirs $(INSTALL) -m 644 $< $@ .PHONY: all clean test install mandirs fwupdate-12/docs/fwup_clear_status.3000066400000000000000000000000231331522106300176520ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_debug_log.3000066400000000000000000000000231331522106300177470ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_esp_mountpoint.3000066400000000000000000000000231331522106300211030ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_fw_type.3000066400000000000000000000000231331522106300174750ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_fw_version.3000066400000000000000000000000231331522106300202010ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_guid.3000066400000000000000000000000231331522106300167500ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_last_attempt_info.3000066400000000000000000000000231331522106300215340ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_lowest_supported_version.3000066400000000000000000000000231331522106300232070ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_get_ux_capsule_info.3000066400000000000000000000000231331522106300212030ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_resource_free.3000066400000000000000000000000231331522106300200110ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_resource_iter_create.3000066400000000000000000000000231331522106300213560ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_resource_iter_destroy.3000066400000000000000000000000231331522106300216040ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_resource_iter_next.3000066400000000000000000000000231331522106300210710ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_set_esp_mountpoint.3000066400000000000000000000000231331522106300211170ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_set_guid.3000066400000000000000000000000231331522106300167640ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_set_guid_forced.3000066400000000000000000000000231331522106300203060ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_set_up_update.3000066400000000000000000000000231331522106300200220ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_supported.3000066400000000000000000000000231331522106300172060ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwup_use_existing_media_path.3000066400000000000000000000000231331522106300220420ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/docs/fwupdate.1000066400000000000000000000037641331522106300157540ustar00rootroot00000000000000.TH FWUPDATE "1" "May 2015" "fwupdate" "User Commands" .SH NAME fwupdate \- update system firmware .SH SYNOPSIS .B fwupdate [\fI\,OPTION\/\fR...] .SH DESCRIPTION .TP \fB\-a\fR, \fB\-\-apply=\fR Apply firmware updates\&. and have to be separates arguments; don't do \fB\-\-apply=\fR" " but \fB\-\-apply=\fR  .TP \fB\-l\fR, \fB\-\-list\fR List supported firmware updates .TP \fB\-s\fR, \fB\-\-supported\fR Query for firmware update support .TP \fB\-i\fR, \fB\-\-info\fR Show the information of firmware update status .TP \fB\-e\fR, \fB\-\-enable\fR Enable firmware update support on supported systems (will require a reboot) .TP \fB\-d\fR | \fB\-D\fR, \fB\-\-set\-debug\fR | \fB\-\-unset\-debug\fR Set or unset the persistent debugging verbosity flag used when running updates. This state is persistent until changed. .TP \fB\-q\fR, \fB\-\-quiet\fR Work quietly .TP \fB\-q\fR, \fB\-\-force\fR Forces flash even if GUID isn't in ESRT (EFI System Resource Table) .TP \fB\-p\fR, \fB\-\-esp-path=\fR Directory where the ESP (EFI System Partition) is currently mounted\&. By default the fwupdate assume the ESP is mounted in /boot/efi/. .TP \fB\-F\fR, \fB\-\-dont-use-existing-media-path\fR By default when you apply a firmware update, \fBfwupdate\fR check the current EFI firmware and recorded file path (use \fB--info\fR to get those informations)\&. If the current EFI firmware is the same as the update, \fBfwupdate\fR will try to use the same recorded file path\&. This can be a problem if, for example, you want to change the \fBESP subdirectory\fR[1] used by \fBfwupdate\fR\&. .sp This option makes \fBfwupdate\fR behave as if there wasn't any recorded file path. .TP \fB\-v\fR, \fB\-\-verbose\fR Be more verbose on errors .SS "Help options:" .TP \fB\-?\fR, \fB\-\-help\fR Show this help message .TP \fB\-\-usage\fR Display brief usage message .SH "NOTES" .IP " 1." 4 ESP subdirectory .RS 4 \%http://www.uefi.org/registry .RE fwupdate-12/docs/libfwup.3000066400000000000000000000132361331522106300156020ustar00rootroot00000000000000.TH LIBFWUP 3 "Mon 11 May 2015" .SH NAME libfwup - library to support management of system firmware updates .SH SYNOPSIS .nf #include <\fBfwup.h\fR> .sp \fBint \fRfwup_supported\fB(void);\fR\p \- test if firmware updating is supported on the running machine \fBtypedef struct fwup_resource_s \fRfwup_resource\fB;\fR\p \fBtypedef struct fwup_resource_iter_s \fRfwup_resource_iter\fB;\fR\p \fBint \fRfwup_resource_iter_create\fB(fwup_resource_iter **\fIiter\fB);\fR\p \fBint \fRfwup_resource_iter_next\fB(\kZfwup_resource_iter *\fIiter\fB, .ta \nZu fwup_resource **\fIre\fB);\fR\p \fBint \fRfwup_resource_iter_destroy\fB(fwup_resource_iter **\fIiter\fB);\fR\p \- iterate the list of updateable firmware images \fBint \fRfwup_set_guid\fB(\kZfwup_resource_iter *\fIiter\fB, fwup_resource **\fIre\fB, .ta \nZu const efi_guid_t *\fIguid\fB);\fR\p \- manually set a guid for update \fBint \fRfwup_set_guid_forced\fB(\kZfwup_resource_iter *\fIiter\fB, fwup_resource **\fIre\fB, .ta \nZu const efi_guid_t *\fIguid\fB, bool \fIforce\fB);\fR\p \- manually set a guid for update (with a force option) \fBvoid \fRfwup_resource_free\fB(fwup_resource *\fIre\fB);\fR\p \- free resource allocated by \fRfwup_set_guid_forced\fB()\fR \fBint \fRfwup_set_up_update\fB(fwup_resource *\fIre\fB, uint64_t \fIhw_inst\fB, int \fIinfd\fB);\fR\p \fBint \fRfwup_clear_status\fB(fwup_resource *\fIre\fB);\fR\p \fBint \fRfwup_get_guid\fB(fwup_resource *\fIre\fB, efi_guid_t **\fIguid\fB);\fR\p \fBint \fRfwup_get_fw_version\fB(fwup_resource *\fIre\fB, uint32_t *\fIversion\fB);\fR\p \fBint \fRfwup_get_fw_type\fB(fwup_resource *\fIre\fB, uint32_t *\fItype\fB);\fR\p \fBint \fRfwup_get_lowest_supported_fw_version\fB(\kZfwup_resource *\fIre\fB, .ta \nZu uint32_t *\fIversion\fB);\fR\p \fBint \fRfwup_get_last_attempt_info\fB(\kZfwup_resource *\fIre\fB, uint32_t *\fIversion\fB, .ta \nZu uint32_t *\fIstatus\fB, time_t *\fIwhen\fB);\fR\p \- operate on an individual firmware entry \fBint \fRfwup_get_debug_log\fB(char **\fIutf8\fB, size_t *\fIsize\fB);\fR\p \- show the debug log from the last update attempt \fBvoid \fRfwup_use_existing_media_path\fB(int \fIuse_existing_media_path\fB);\fR\p \- instruct fwupdate on whether to reuse old capsule filenames on the ESP. \fBvoid \fRfwup_set_esp_mountpoint\fB(char *\fIpath\fB);\fR\p \- Override detection of the mountpoint for the ESP directory \fBconst char *\fRfwup_get_esp_mountpoint\fB(void);\fR\p \- Get the current mountpoint for the ESP directory \fBint \fRfwup_get_ux_capsule_info\fB(\kZuint32_t *\fIscreen_x_size\fB, .tz \nZu uint32_t *\fIscreen_y_size\fB);\fR\p \- get the dimensions of the screen during boot .SH DESCRIPTION .nf .PP \fBint \fRfwup_supported\fB(void);\fR Tests if the current machine supports firmware updates .PP \fBtypedef struct fwup_resource_s \fRfwup_resource\fB;\fR\p \fBtypedef struct fwup_resource_iter_s \fRfwup_resource_iter\fB;\fR\p \fBint \fRfwup_resource_iter_create\fB(fwup_resource_iter **\fIiter\fB);\fR\p Create a new firmware resoure iterator \fIiter\fR. \fBint \fRfwup_resource_iter_next\fB(\kZfwup_resource_iter *\fIiter\fB, .ta \nZu fwup_resource **\fIre\fB);\fR\p Get the next firmware resource \fIre\fR from iterator \fIiter\fR. \fBint \fRfwup_resource_iter_destroy\fB(fwup_resource_iter **\fIiter\fB);\fR\p Destroy firmware resource iterator \fIiter\fR. \fBint \fRfwup_set_up_update\fB(fwup_resource *\fIre\fB, uint64_t \fIhw_inst\fB, int \fIinfd\fB);\fR\p Set up an update for resource \fIre\fR, hardware instance number \fIhw_inst\fR, with the file referenced by the file descriptor \fIinfd\fR. \fIinfd\fR must support read\fB(3)\fR. \fBint \fRfwup_clear_status\fB(fwup_resource *\fIre\fB);\fR\p Clear the status of the firmware resource \fIre\fR. This removes any pending attempt to update it, as well as clearing any pending error report. \fBint \fRfwup_get_guid\fB(fwup_resource *\fIre\fB, efi_guid_t **\fIguid\fB);\fR\p Set \fIguid\fR to the GUID which uniquely identifies firmware resource \fIre\fR. \fBint \fRfwup_get_fw_version\fB(fwup_resource *\fIre\fB, uint32_t *\fIversion\fB);\fR\p Set \fIversion\fR to the version of the firmware resource \fIre\fR. #define \fBFWUP_RESOURCE_TYPE_UNKNOWN\fR 0 #define \fBFWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE\fR 1 #define \fBFWUP_RESOURCE_TYPE_DEVICE_FIRMWARE\fR 2 #define \fBFWUP_RESOURCE_TYPE_UEFI_DRIVER\fR 3 \fBint \fRfwup_get_fw_type\fB(fwup_resource *\fIre\fB, uint32_t *\fItype\fB);\fR\p Set \fItype\fR to the type of the firmware resource \fIre\fR. \fBint \fRfwup_get_lowest_supported_fw_version\fB(\kZfwup_resource *\fIre\fB, .ta \nZu uint32_t *\fIversion\fB);\fR Set \fIversion\fR to the lowest firmware version resource \fIre\fR can be updated to. \fBint \fRfwup_get_last_attempt_info\fB(\kZfwup_resource *\fIre\fB, uint32_t *\fIversion\fB, .ta \nZu uint32_t *\fIstatus\fB, time_t *\fIwhen\fB);\fR\p Get the status for the last attempt to update firmware resource \fIre\fR. \fBint \fRfwup_get_debug_log\fB(char **\fIutf8\fB, size_t *\fIsize\fB);\fR\p Get the debug log from the last update attempt. This allocates a buffer which the caller must free. \fBvoid \fRfwup_print_update_info\fB(void);\fR\p Print the information of firmware update status. \fBvoid \fRfwup_use_existing_media_path\fB(int \fIuse_existing_media_path\fB);\fR\p Instruct fwupdate on whether to reuse old capsule filenames on the ESP. \fBvoid \fRfwup_set_esp_mountpoint\fB(char *\fIpath\fB);\fR\p Override detection of the mountpoint for the ESP directory. \fBint \fRfwup_get_ux_capsule_info\fB(\kZuint32_t *\fIscreen_x_size\fB, .tz \nZu uint32_t *\fIscreen_y_size\fB);\fR\p Get the dimensions of the screen during boot. .SH AUTHORS .nf Peter Jones .SH SEE ALSO \fBerrno\fR(3), \fBstrerror\fR(3), \fBstrerror_r\fR(3) fwupdate-12/docs/libfwup.h.3000066400000000000000000000000231331522106300160160ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-12/efi/000077500000000000000000000000001331522106300136545ustar00rootroot00000000000000fwupdate-12/efi/Makefile000066400000000000000000000106661331522106300153250ustar00rootroot00000000000000default : all ifneq ($(origin TOPDIR),undefined) TOP := $(abspath $(TOPDIR)) else TOP := $(abspath ..) endif include $(TOP)/Make.defaults include $(TOP)/Make.rules VPATH = $(TOP)/efi CFLAGS ?= -Og -g3 -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 \ -grecord-gcc-switches BUILDFLAGS := $(CFLAGS) -fpic -Werror -Wall -Wextra -fshort-wchar \ -Wno-error=missing-field-initializers -Wno-missing-field-initializers \ -fno-merge-constants -ffreestanding \ -fno-stack-protector -fno-stack-check --std=gnu11 -DCONFIG_$(ARCH) \ -I/usr/include/efi/ -I/usr/include/efi/$(ARCH)/ \ -iquote$(TOP)/include "-DDEBUGDIR=L\"$(DEBUGDIR)/\"" CCLDFLAGS ?= -nostdlib -Wl,--warn-common \ -Wl,--no-undefined -Wl,--fatal-warnings \ -Wl,-shared -Wl,-Bsymbolic -L$(LIBDIR) -L$(GNUEFIDIR) \ -Wl,--build-id=sha1 -Wl,--hash-style=sysv \ $(GNUEFIDIR)/crt0-efi-$(ARCH).o CLANG_BUGS = $(if $(findstring gcc,$(CC)),-maccumulate-outgoing-args,) LIBGCC=$(shell $(CC) -print-libgcc-file-name) ifeq ($(ARCH),arm) LIBGCC+=" $(shell $(CC) -print-libgcc-file-name | sed 's/\.a$/_eh.a/')" endif define objcopy_version = $(OBJCOPY) --version | sed -e '/^GNU objcopy/! d; : loop1; s/([^)(]*)//g; t loop1; : loop2; s/\[[^][]*\]//g; t loop2; s/.* \([0-9][0-9]*\.[0-9][0-9]*\).*/\1/;' endef OBJCOPY_GTE224 := $(shell expr `$(objcopy_version)` \>= 2.24) FWUP = fwupdate ifeq ($(ARCH),x86_64) FORMAT = --target efi-app-$(ARCH) BUILDFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc \ $(CLANG_BUGS) -DEFI_FUNCTION_WRAPPER \ -DGNU_EFI_USE_MS_ABI -I$(shell $(CC) -print-file-name=include) FWUP = fwupx64 endif ifeq ($(ARCH),ia32) FORMAT = --target efi-app-$(ARCH) BUILDFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc \ $(CLANG_BUGS) -m32 \ -I$(shell $(CC) -print-file-name=include) FWUP = fwupia32 endif ifeq ($(ARCH),aarch64) FORMAT = -O binary CCLDFLAGS += -Wl,--defsym=EFI_SUBSYSTEM=0xa BUILDFLAGS += -ffreestanding -I$(shell $(CC) -print-file-name=include) FWUP = fwupaa64 endif ifeq ($(ARCH),arm) FORMAT = -O binary CCLDFLAGS += -Wl,--defsym=EFI_SUBSYSTEM=0xa BUILDFLAGS += -ffreestanding -I$(shell $(CC) -print-file-name=include) FWUP = fwuparm endif TARGETS = fakeesrt2.efi fakeesrt.efi dumpesrt.efi $(FWUP).efi mkvar.efi dumpf.efi mkvar2.efi all : $(TARGETS) abidw abicheck : .SECONDARY: $(foreach target,$(TARGETS),$(target).debug \ $(target).build-id \ $(target).so \ ) $(FWUP).so : fwupdate.so ln -f $< $@ $(FWUP).efi : | $(FWUP).so %.efi.signed : %.efi %.efi.debug %.efi.build-id pesign -s -i $< -o $@ --force -c 'Red Hat Test Certificate' %.efi : %.so ifneq ($(OBJCOPY_GTE224),1) $(error objcopy >= 2.24 is required) endif $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym \ -j .rel* -j .rela* -j .reloc -j .eh_frame \ $(FORMAT) $^ $@ %.efi.debug : %.so ifneq ($(OBJCOPY_GTE224),1) $(error objcopy >= 2.24 is required) endif $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym \ -j .rel* -j .rela* -j .reloc -j .eh_frame \ -j .debug* -j .note.gnu.build-id \ $^ $@ %.efi.build-id : %.so $(READELF) -n $^ | grep "Build ID:" | \ sed -e 's/^.*Build ID: //' -e 's,^\(..\),\1/,' > $@ %.so : %.o $(CC) $(CCLDFLAGS) -o $@ $^ -lefi -lgnuefi \ $(LIBGCC) \ -T elf_$(ARCH)_efi.lds %.o : %.c $(CC) $(BUILDFLAGS) -c -o $@ $^ clean : @rm -vf $(TARGETS) *.o *.so *.efi.signed *.efi *.efi.debug \ *.efi.build-id buildiddir = $(dir $(shell cat $(1).build-id)) buildidname = $(notdir $(shell cat $(1).build-id)) define inst = $(INSTALL) -d -m 755 $(DESTDIR)$(TARGETDIR)/ $(INSTALL) -m 755 $(1) $(DESTDIR)$(TARGETDIR)/$(1) $(INSTALL) -d -m 755 $(DESTDIR)$(DEBUGINFO)$(TARGETDIR)/ $(INSTALL) -m 755 $(1).debug $(DESTDIR)$(DEBUGINFO)$(TARGETDIR)/$(1).debug $(INSTALL) -d -m 755 $(DESTDIR)$(DEBUGINFO)/.build-id/$(buildiddir) ln -sf ../../../../$(TARGETDIR)/$(1) $(DESTDIR)$(DEBUGINFO)/.build-id/$(buildiddir)/$(buildidname) ln -sf ../../$(TARGETDIR)$(1).debug $(DESTDIR)$(DEBUGINFO)/.build-id/$(buildiddir)/$(buildidname).debug endef install : | check_efidir_error install : $(FWUP).efi | $(FWUP).efi.debug $(FWUP).efi.build-id $(INSTALL) -d -m 755 $(DESTDIR)$(TARGETDIR)/fw/ $(foreach prog,$^,$(call inst,$(prog)) ; ) $(INSTALL) -d -m 755 $(DESTDIR)$(DEBUGSOURCE)/fwupdate-$(VERSION)$(DASHRELEASE)$(DOTARCH)/efi/ $(foreach src,$(wildcard *.[chS]),$(INSTALL) -m 644 $(src) $(DESTDIR)$(DEBUGSOURCE)/fwupdate-$(VERSION)$(DASHRELEASE)$(DOTARCH)/efi/$(src) ;) fwupdate-12/efi/dumpesrt.c000066400000000000000000000033551331522106300156710ustar00rootroot00000000000000#include #include typedef struct esrt { UINT32 fw_resource_count; UINT32 fw_resource_count_max; UINT64 version; } __attribute__((__packed__)) esrt_t; typedef struct esre1 { EFI_GUID fw_class; UINT32 fw_type; UINT32 fw_version; UINT32 lowest_supported_fw_version; UINT32 capsule_flags; UINT32 last_attempt_version; UINT32 last_attempt_status; } __attribute__((__packed__)) esre1_t; #define esrt_guid { 0xb122a263, 0x3661, 0x4f68, { 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 }} static EFI_GUID EsrtGuid = esrt_guid; #include "hexdump.h" static void dump_esrt(VOID *data) { esrt_t *esrt = data; esre1_t *esre1 = (esre1_t *)((UINT8 *)data + sizeof (*esrt)); hexdump(data, sizeof(*esrt)); for (unsigned int i = 0; i < esrt->fw_resource_count; i++) { if (esrt->version == 1) { Print(L"{%g}:\n", &esre1->fw_class); Print(L" type:%d fv:0x%08x lsfv:0x%08x fl:0x%08x ", esre1->fw_type, esre1->fw_version, esre1->lowest_supported_fw_version, esre1->capsule_flags); Print(L"lav: 0x%08x las: %d\n", esre1->last_attempt_version, esre1->last_attempt_status); hexdump((void *)esre1, sizeof (*esre1)); esre1++; } else { Print(L"Weird ESRT version %d\n", esrt->version); } } } EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) { EFI_CONFIGURATION_TABLE *ect = systab->ConfigurationTable; InitializeLib(image_handle, systab); for (unsigned int i = 0; i < systab->NumberOfTableEntries; i++) { if (CompareMem(&ect->VendorGuid, &EsrtGuid, sizeof(EsrtGuid))) { ect++; continue; } Print(L"Dumping VendorTable at %016x\n", ect->VendorTable); dump_esrt(ect->VendorTable); return 0; } Print(L"ESRT not found.\n"); return 0; } fwupdate-12/efi/dumpf.c000066400000000000000000000006601331522106300151350ustar00rootroot00000000000000#include #include #include "hexdump.h" EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) { InitializeLib(image_handle, systab); hexdump((void *)0xE0000000, 128); hexdump((void *)0xFEB00000, 128); hexdump((void *)0xFEC00000, 128); hexdump((void *)0xFED10000, 128); hexdump((void *)0xFED1C000, 128); hexdump((void *)0xFEE00000, 128); hexdump((void *)0xFFD00000, 128); return 0; } fwupdate-12/efi/elf_aarch64_efi.lds000066400000000000000000000021541331522106300172630ustar00rootroot00000000000000OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") OUTPUT_ARCH(aarch64) ENTRY(_start) SECTIONS { .text 0x0 : { _text = .; *(.text.head) *(.text) *(.text.*) *(.gnu.linkonce.t.*) *(.srodata) *(.rodata*) . = ALIGN(16); _etext = .; } .dynamic : { *(.dynamic) } .data : { _data = .; *(.sdata) *(.data) *(.data1) *(.data.*) *(.got.plt) *(.got) /* the EFI loader doesn't seem to like a .bss section, so we stick it all into .data: */ . = ALIGN(16); _bss = .; *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) . = ALIGN(16); _bss_end = .; } .note.gnu.build-id : { *(.note.gnu.build-id) } .rela.dyn : { *(.rela.dyn) } .rela.plt : { *(.rela.plt) } .rela.got : { *(.rela.got) } .rela.data : { *(.rela.data) *(.rela.data*) } _edata = .; _data_size = . - _etext; . = ALIGN(4096); .dynsym : { *(.dynsym) } . = ALIGN(4096); .dynstr : { *(.dynstr) } . = ALIGN(4096); /DISCARD/ : { *(.rel.reloc) *(.eh_frame) *(.note.GNU-stack) } .comment 0 : { *(.comment) } } fwupdate-12/efi/elf_arm_efi.lds000066400000000000000000000021521331522106300166100ustar00rootroot00000000000000OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { .text 0x0 : { _text = .; *(.text.head) *(.text) *(.text.*) *(.gnu.linkonce.t.*) *(.srodata) *(.rodata*) . = ALIGN(16); } _etext = .; _text_size = . - _text; .dynamic : { *(.dynamic) } .data : { _data = .; *(.sdata) *(.data) *(.data1) *(.data) *(.got.plt) *(.got) /* the EFI loader doesn't seem to like a .bss section, so we stick it all into .data: */ . = ALIGN(16); _bss = .; *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) . = ALIGN(16); _bss_end = .; } .note.gnu.build-id : { *(.note.gnu.build-id) } .rel.dyn : { *(.rel.dyn) } .rel.plt : { *(.rel.plt) } .rel.got : { *(.rel.got) } .rel.data : { *(.rel.data) *(.rel.data*) } _edata = .; _data_size = . - _etext; . = ALIGN(4096); .dynsym : { *(.dynsym) } . = ALIGN(4096); .dynstr : { *(.dynstr) } . = ALIGN(4096); /DISCARD/ : { *(.rel.reloc) *(.eh_frame) *(.note.GNU-stack) } .comment 0 : { *(.comment) } } fwupdate-12/efi/elf_ia32_efi.lds000066400000000000000000000025741331522106300165770ustar00rootroot00000000000000OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { . = 0; ImageBase = .; .hash : { *(.hash) } /* this MUST come first! */ .gnu.hash : { *(.gnu.hash) } . = ALIGN(4096); .text : { _text = .; *(.text) *(.text.*) *(.gnu.linkonce.t.*) _etext = .; } . = ALIGN(4096); .sdata : { *(.got.plt) *(.got) *(.srodata) *(.sdata) *(.sbss) *(.scommon) } . = ALIGN(4096); .data : { _data = .; *(.rodata*) *(.data) *(.data1) *(.data.*) *(.sdata) *(.got.plt) *(.got) /* the EFI loader doesn't seem to like a .bss section, so we stick it all into .data: */ *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) } .note.gnu.build-id : { *(.note.gnu.build-id) } . = ALIGN(4096); .dynamic : { *(.dynamic) } . = ALIGN(4096); .rel : { *(.rel.data) *(.rel.data.*) *(.rel.got) *(.rel.stab) *(.data.rel.ro.local) *(.data.rel.local) *(.data.rel.ro) *(.data.rel*) } . = ALIGN(4096); .reloc : /* This is the PECOFF .reloc section! */ { *(.reloc) } _edata = .; _data_size = . - _etext; . = ALIGN(4096); .dynsym : { *(.dynsym) } . = ALIGN(4096); .dynstr : { *(.dynstr) } . = ALIGN(4096); /DISCARD/ : { *(.rel.reloc) *(.eh_frame) *(.note.GNU-stack) } .comment 0 : { *(.comment) } } fwupdate-12/efi/elf_x86_64_efi.lds000066400000000000000000000024201331522106300167650ustar00rootroot00000000000000/* Same as elf_x86_64_fbsd_efi.lds, except for OUTPUT_FORMAT below - KEEP IN SYNC */ OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") OUTPUT_ARCH(i386:x86-64) ENTRY(_start) SECTIONS { . = 0; ImageBase = .; .hash : { *(.hash) } /* this MUST come first! */ .gnu.hash : { *(.gnu.hash) } . = ALIGN(4096); .eh_frame : { *(.eh_frame) } . = ALIGN(4096); .text : { _text = .; *(.text) *(.text.*) *(.gnu.linkonce.t.*) _etext = .; } . = ALIGN(4096); .reloc : { *(.reloc) } . = ALIGN(4096); .data : { _data = .; *(.rodata*) *(.got.plt) *(.got) *(.data*) *(.sdata) /* the EFI loader doesn't seem to like a .bss section, so we stick it all into .data: */ *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) *(.rel.local) } .note.gnu.build-id : { *(.note.gnu.build-id) } . = ALIGN(4096); .dynamic : { *(.dynamic) } . = ALIGN(4096); .rela : { *(.rela.data*) *(.rela.got) *(.rela.stab) } _edata = .; _data_size = . - _etext; . = ALIGN(4096); .dynsym : { *(.dynsym) } . = ALIGN(4096); .dynstr : { *(.dynstr) } . = ALIGN(4096); .ignored.reloc : { *(.rela.reloc) *(.eh_frame) *(.note.GNU-stack) } .comment 0 : { *(.comment) } } fwupdate-12/efi/fakeesrt.c000066400000000000000000000036071331522106300156320ustar00rootroot00000000000000#include #include typedef struct esrt { UINT32 fw_resource_count; UINT32 fw_resource_count_max; UINT64 version; } esrt_t; typedef struct esre { EFI_GUID fw_class; UINT32 fw_type; UINT32 fw_version; UINT32 lowest_supported_fw_version; UINT32 capsule_flags; UINT32 last_attempt_version; UINT32 last_attempt_status; } esre_t; #define guid0 { 0x0712233d, 0xfe15, 0x434c, { 0xbf, 0x4d, 0xa3, 0x4a, 0x05, 0x03, 0x14, 0x4a }} #define guid1 { 0xeac48586, 0xebf7, 0x4901, { 0xb2, 0x32, 0x0b, 0x29, 0xe9, 0x9a, 0xe6, 0xa9 }} #define esrt_guid { 0xb122a263, 0x3661, 0x4f68, { 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 }} static EFI_GUID EsrtGuid = esrt_guid; EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) { struct { esrt_t esrt; esre_t esre0; esre_t esre1; } __attribute__((packed)) esrt = { .esrt = { 2, 2, 1 }, .esre0 = { guid0, 0, 0, 0, 0x80f1, 0, 0 }, .esre1 = { guid1, 1, 9, 7, 0x80f1, 0, 0 }, }; EFI_STATUS status; EFI_PHYSICAL_ADDRESS mem = 0; EFI_ALLOCATE_TYPE type = AllocateAnyPages; InitializeLib(image_handle, systab); if (sizeof (VOID *) == 4) { mem = 0xffffffffULL - 8192; type = AllocateMaxAddress; } status = uefi_call_wrapper(systab->BootServices->AllocatePages, 4, type, EfiRuntimeServicesData, 1, &mem); if (EFI_ERROR(status)) { Print(L"AllocatePages failed: %r\n", status); return status; } if (sizeof (VOID *) == 4 && mem > 0xffffffffULL) { Print(L"Got bad allocation at 0x%016x\n", (UINT64)mem); return EFI_OUT_OF_RESOURCES; } VOID *ptr = (VOID *)(UINTN)mem; CopyMem(ptr, &esrt, sizeof (esrt)); status = uefi_call_wrapper( systab->BootServices->InstallConfigurationTable, 2, &EsrtGuid, ptr); if (EFI_ERROR(status)) { Print(L"InstallConfigurationTable failed: %r\n", status); uefi_call_wrapper(systab->BootServices->FreePages, 2, mem, 1); return status; } return 0; } fwupdate-12/efi/fakeesrt2.c000066400000000000000000000030411331522106300157040ustar00rootroot00000000000000#include #include typedef struct esre { EFI_GUID fw_class; UINT32 fw_type; UINT32 fw_version; UINT32 lowest_supported_fw_version; UINT32 capsule_flags; UINT32 last_attempt_version; UINT32 last_attempt_status; } __attribute__ ((__packed__)) esre_t; #define guid0 { 0x0712233d, 0xfe15, 0x434c, { 0xbf, 0x4d, 0xa3, 0x4a, 0x05, 0x03, 0x14, 0x4a }} #define guid1 { 0xeac48586, 0xebf7, 0x4901, { 0xb2, 0x32, 0x0b, 0x29, 0xe9, 0x9a, 0xe6, 0xa9 }} #define fake_capsule_header_guid {0x32e678e9, 0x263f, 0x4eae, {0xa2, 0x39, 0x38, 0xa3, 0xca, 0xd6, 0xa1, 0xb5}} EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) { esre_t esre0 = { guid0, 0, 0, 0, 0x80f1, 0, 0 }; esre_t esre1 = { guid1, 1, 9, 7, 0x80f1, 0, 0 }; EFI_GUID fchg = fake_capsule_header_guid; EFI_STATUS status; InitializeLib(image_handle, systab); status = systab->RuntimeServices->SetVariable ( L"Esrt0000", &fchg, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (esre0), &esre0); if (EFI_ERROR(status)) { Print(L"SetVariable(esre0) failed: %r\n", status); return status; } status = systab->RuntimeServices->SetVariable ( L"Esrt0001", &fchg, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (esre1), &esre1); if (EFI_ERROR(status)) { Print(L"SetVariable(esre1) failed: %r\n", status); return status; } return 0; } fwupdate-12/efi/fwupdate.c000066400000000000000000001007411331522106300156420ustar00rootroot00000000000000/* * fwupdate.c - apply firmware updates * * Copyright 2014 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #include #include #include #include "fwup-efi.h" #include "hexdump.h" #define UNUSED __attribute__((__unused__)) EFI_GUID empty_guid = {0x0,0x0,0x0,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}}; EFI_GUID fwupdate_guid = {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; EFI_GUID ux_capsule_guid = {0x3b8c8162,0x188c,0x46a4,{0xae,0xc9,0xbe,0x43,0xf1,0xd6,0x56,0x97}}; EFI_GUID fmp_capsule_guid = {0x6dcbd5ed,0xe82d,0x4c44,{0xbd,0xa1,0x71,0x94,0x19,0x9a,0xd9,0x2a}}; EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE; typedef struct update_table_s { CHAR16 *name; UINT32 attributes; UINTN size; update_info *info; } update_table; static EFI_STATUS delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes); static EFI_STATUS set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size, UINT32 attrs); static int debugging; #define SECONDS 1000000 static VOID msleep(unsigned long msecs) { BS->Stall(msecs); } /* * I'm not actually sure when these appear, but they're present in the * version in front of me. */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) #if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 #define uintn_mult(a, b, c) __builtin_mul_overflow(a, b, c) #endif #endif #ifndef uintn_mult #define uintn_mult(a, b, c) ({ \ const UINTN _limit = ~0UL; \ int _ret = 1; \ if ((a) != 0 && (b) != 0) { \ _ret = _limit / (a) < (b); \ } \ if (!_ret) \ *(c) = ((a) * (b)); \ _ret; \ }) #endif int debug_print(const char *func, const char *file, const int line, CHAR16 *fmt, ...) { va_list args0, args1; CHAR16 *out0, *out1; UINT32 attrs = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; CHAR16 *name = L"FWUPDATE_DEBUG_LOG"; static bool once = true; va_start(args0, fmt); out0 = VPoolPrint(fmt, args0); va_end(args0); if (!out0) { if (debugging) { va_start(args1, fmt); VPrint(fmt, args1); va_end(args1); msleep(200000); } Print(L"fwupdate: Allocation for debug log failed!\n"); return debugging; } if (debugging) Print(L"%s", out0); out1 = PoolPrint(L"%a:%d:%a(): %s", file, line, func, out0); FreePool(out0); if (!out1) { Print(L"fwupdate: Allocation for debug log failed!\n"); return debugging; } if (once) { once = false; delete_variable(name, fwupdate_guid, attrs); } else { attrs |= EFI_VARIABLE_APPEND_WRITE; } set_variable(name, fwupdate_guid, out1, StrSize(out1) - sizeof (CHAR16), attrs); FreePool(out1); return debugging; } #define dprint(fmt, args...) debug_print(__func__, __FILE__, __LINE__, fmt, ## args ) #define print(fmt, args...) ({ if (!dprint(fmt, ## args)) Print(fmt, ## args); }) /* * Allocate some raw pages that aren't part of the pool allocator. */ static EFI_STATUS allocate(void **addr, UINTN size) { /* * We're actually guaranteed that page size is 4096 by UEFI. */ UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); EFI_STATUS rc; EFI_PHYSICAL_ADDRESS pageaddr = 0; EFI_ALLOCATE_TYPE type = AllocateAnyPages; if (sizeof (VOID *) == 4) { pageaddr = 0xffffffffULL - 8192; type = AllocateMaxAddress; } rc = uefi_call_wrapper(BS->AllocatePages, 4, type, EfiLoaderData, pages, &pageaddr); if (EFI_ERROR(rc)) return rc; if (sizeof (VOID *) == 4 && pageaddr > 0xffffffffULL) { uefi_call_wrapper(BS->FreePages, 2, pageaddr, pages); print(L"Got bad allocation at 0x%016x\n", (UINT64)pageaddr); return EFI_OUT_OF_RESOURCES; } *addr = (void *)(UINTN)pageaddr; return rc; } /* * Free our raw page allocations. */ static EFI_STATUS free(void *addr, UINTN size) { UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0); EFI_STATUS rc; rc = uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)(UINTN)addr, pages); return rc; } static inline int guid_cmp(efi_guid_t *a, efi_guid_t *b) { return CompareMem(a, b, sizeof (*a)); } EFI_STATUS read_file(EFI_FILE_HANDLE fh, UINT8 **buf_out, UINTN *buf_size_out) { UINT8 *b = NULL; const UINTN bs = 512; UINTN n_blocks = 4096; UINTN i = 0; EFI_STATUS rc; while (1) { void *newb = NULL; UINTN news = 0; if (uintn_mult(bs * 2, n_blocks, &news)) { if (b) free(b, bs * n_blocks); print(L"allocation %d * %d would overflow size\n", bs * 2, n_blocks); return EFI_OUT_OF_RESOURCES; } rc = allocate(&newb, news); if (EFI_ERROR(rc)) { print(L"Tried to allocate %d\n", bs * n_blocks * 2); print(L"Could not allocate memory.\n"); return EFI_OUT_OF_RESOURCES; } if (b) { CopyMem(newb, b, bs * n_blocks); free(b, bs * n_blocks); } b = newb; n_blocks *= 2; for (; i < n_blocks; i++) { EFI_STATUS rc; UINTN sz = bs; rc = uefi_call_wrapper(fh->Read, 3, fh, &sz, &b[i * bs]); if (EFI_ERROR(rc)) { free(b, bs * n_blocks); print(L"Could not read file: %r\n", rc); return rc; } if (sz != bs) { *buf_size_out = bs * i + sz; *buf_out = b; return EFI_SUCCESS; } } } return EFI_SUCCESS; } static EFI_STATUS delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes) { return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attributes, 0, NULL); } static EFI_STATUS set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size, UINT32 attrs) { return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attrs, size, data); } static EFI_STATUS read_variable(CHAR16 *name, EFI_GUID guid, void **buf_out, UINTN *buf_size_out, UINT32 *attributes_out) { EFI_STATUS rc; UINT32 attributes; UINTN size = 0; void *buf = NULL; rc = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes, &size, NULL); if (EFI_ERROR(rc)) { if (rc == EFI_BUFFER_TOO_SMALL) { buf = AllocatePool(size); if (!buf) { print(L"Tried to allocate %d\n", size); print(L"Could not allocate memory.\n"); return EFI_OUT_OF_RESOURCES; } } else if (rc != EFI_NOT_FOUND) { print(L"Could not get variable \"%s\": %r\n", name, rc); return rc; } } else { print(L"GetVariable(%s) succeeded with size=0.\n", name); return EFI_INVALID_PARAMETER; } rc = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes, &size, buf); if (EFI_ERROR(rc)) { print(L"Could not get variable \"%s\": %r\n", name, rc); FreePool(buf); return rc; } *buf_out = buf; *buf_size_out = size; *attributes_out = attributes; return EFI_SUCCESS; } static INTN dp_size(EFI_DEVICE_PATH *dp, INTN limit) { INTN ret = 0; while (1) { if (limit < 4) break; INTN nodelen = DevicePathNodeLength(dp); if (nodelen > limit) break; limit -= nodelen; ret += nodelen; if (IsDevicePathEnd(dp)) return ret; dp = NextDevicePathNode(dp); } return -1; } static EFI_STATUS get_info(CHAR16 *name, update_table *info_out) { EFI_STATUS rc; update_info *info = NULL; UINTN info_size = 0; UINT32 attributes = 0; void *info_ptr = NULL; rc = read_variable(name, fwupdate_guid, &info_ptr, &info_size, &attributes); if (EFI_ERROR(rc)) return rc; info = (update_info *)info_ptr; if (info_size < sizeof (*info)) { print(L"Update \"%s\" is is too small.\n", name); delete_variable(name, fwupdate_guid, attributes); return EFI_INVALID_PARAMETER; } if (info_size - sizeof (EFI_DEVICE_PATH) <= sizeof (*info)) { print(L"Update \"%s\" is malformed, " L"and cannot hold a file path.\n", name); delete_variable(name, fwupdate_guid, attributes); return EFI_INVALID_PARAMETER; } EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp; INTN is = EFI_FIELD_OFFSET(update_info, dp); if (is > (INTN)info_size) { print(L"Update \"%s\" has an invalid file path.\n" L"Device path offset is %d, but total size is %d\n", name, is, info_size); delete_variable(name, fwupdate_guid, attributes); return EFI_INVALID_PARAMETER; } is = info_size - is; INTN sz = dp_size(hdr, info_size); if (sz < 0 || is < 0) { invalid_size: print(L"Update \"%s\" has an invalid file path.\n" L"update info size: %d dp size: %d size for dp: %d\n", name, info_size, sz, is); delete_variable(name, fwupdate_guid, attributes); return EFI_INVALID_PARAMETER; } if (is > (INTN)info_size) goto invalid_size; if (is != sz) goto invalid_size; info_out->info = info; info_out->size = info_size; info_out->attributes = attributes; return EFI_SUCCESS; } static EFI_STATUS find_updates(UINTN *n_updates_out, update_table ***updates_out) { EFI_STATUS rc; update_table **updates = NULL; UINTN n_updates = 0; UINTN n_updates_allocated = 128; EFI_STATUS ret = EFI_OUT_OF_RESOURCES; #define GNVN_BUF_SIZE 1024 UINTN variable_name_allocation = GNVN_BUF_SIZE; UINTN variable_name_size = 0; CHAR16 *variable_name; EFI_GUID vendor_guid = empty_guid; UINTN mult_res; if (uintn_mult(sizeof (update_table *), n_updates_allocated, &mult_res)) { print(L"Allocation %d * %d would overflow size\n", sizeof (update_table *), n_updates_allocated); return EFI_OUT_OF_RESOURCES; } updates = AllocateZeroPool(mult_res); if (!updates) { print(L"Tried to allocate %d\n", mult_res); print(L"Could not allocate memory.\n"); return EFI_OUT_OF_RESOURCES; } /* How much do we trust "size of the VariableName buffer" to mean * sizeof(vn) and not sizeof(vn)/sizeof(vn[0]) ? */ variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2); if (!variable_name) { print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2); print(L"Could not allocate memory.\n"); FreePool(updates); return EFI_OUT_OF_RESOURCES; } while (1) { variable_name_size = variable_name_allocation; rc = uefi_call_wrapper(RT->GetNextVariableName, 3, &variable_name_size, variable_name, &vendor_guid); if (rc == EFI_BUFFER_TOO_SMALL) { /* If we don't have a big enough buffer to hold the * name, allocate a bigger one and try again */ UINTN new_allocation; CHAR16 *new_name; new_allocation = variable_name_size; if (uintn_mult(new_allocation, 2, &mult_res)) { print(L"%d * 2 would overflow size\n", new_allocation); ret = EFI_OUT_OF_RESOURCES; goto err; } new_name = AllocatePool(new_allocation * 2); if (!new_name) { print(L"Tried to allocate %d\n", new_allocation * 2); print(L"Could not allocate memory.\n"); ret = EFI_OUT_OF_RESOURCES; goto err; } CopyMem(new_name, variable_name, variable_name_allocation); variable_name_allocation = new_allocation; FreePool(variable_name); variable_name = new_name; continue; } else if (rc == EFI_NOT_FOUND) { break; } else if (EFI_ERROR(rc)) { print(L"Could not get variable name: %r\n", rc); ret = rc; goto err; } /* * If it's not one of our state variables, keep going. */ if (guid_cmp(&vendor_guid, &fwupdate_guid)) continue; /* * Don't delete our debugging settings. */ if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 || StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0) continue; UINTN vns = StrLen(variable_name); CHAR16 vn[vns + 1]; CopyMem(vn, variable_name, vns * sizeof (vn[0])); vn[vns] = L'\0'; print(L"Found update %s\n", vn); if (n_updates == n_updates_allocated) { update_table **new_ups; UINTN mul_a, mul_b; if (uintn_mult(n_updates_allocated, 2, &mult_res)) { mul_a = n_updates_allocated; mul_b = 2; mult_err: print(L"Allocation %d * %d would overflow size\n", mul_a, mul_b); ret = EFI_OUT_OF_RESOURCES; goto err; } if (uintn_mult(mult_res, sizeof (update_table *), &mult_res)) { mul_a = mult_res; mul_b = sizeof (update_table *); goto mult_err; } new_ups = AllocateZeroPool(mult_res); if (!new_ups) { print(L"Tried to allocate %d\n", mult_res); print(L"Could not allocate memory.\n"); ret = EFI_OUT_OF_RESOURCES; goto err; } CopyMem(new_ups, updates, mult_res); n_updates_allocated *= 2; FreePool(updates); updates = new_ups; } update_table *update = AllocatePool(sizeof (update_table)); if (!update) { print(L"Tried to allocate %d\n", sizeof (update_table)); ret = EFI_OUT_OF_RESOURCES; goto err; } update->name = StrDuplicate(vn); if (!update->name) { print(L"Tried to allocate %d\n", StrSize(vn)); ret = EFI_OUT_OF_RESOURCES; FreePool(update); goto err; } rc = get_info(vn, update); if (EFI_ERROR(rc)) { print(L"Could not get update info for \"%s\", aborting.\n", vn); ret = rc; FreePool(update->name); FreePool(update); goto err; } if (update->info->status & FWUPDATE_ATTEMPT_UPDATE) { EFI_TIME_CAPABILITIES timecaps = { 0, }; uefi_call_wrapper(RT->GetTime, 2, &update->info->time_attempted, &timecaps); update->info->status = FWUPDATE_ATTEMPTED; updates[n_updates++] = update; } else { FreePool(update->info); FreePool(update->name); FreePool(update); } } FreePool(variable_name); *n_updates_out = n_updates; *updates_out = updates; return EFI_SUCCESS; err: FreePool(variable_name); for (unsigned int i = 0; i < n_updates; i++) { FreePool(updates[i]->name); FreePool(updates[i]->info); FreePool(updates[i]); } FreePool(updates); return ret; } static EFI_STATUS search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh) { EFI_DEVICE_PATH *dp, *parent_dp; EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID dpp = DEVICE_PATH_PROTOCOL; EFI_FILE_HANDLE *devices; UINTN i, n_handles, count; EFI_STATUS rc; rc = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &sfsp, NULL, &n_handles, (EFI_HANDLE **)&devices); if (EFI_ERROR(rc)) { print(L"Could not find handles.\n"); return rc; } dp = *file_dp; if (debugging) print(L"Searching Device Path: %s ...\n", DevicePathToStr(dp)); parent_dp = DuplicateDevicePath(dp); if (!parent_dp) { rc = EFI_INVALID_PARAMETER; goto out; } dp = parent_dp; count = 0; while (1) { if (IsDevicePathEnd(dp)) { rc = EFI_INVALID_PARAMETER; goto out; } if (DevicePathType(dp) == MEDIA_DEVICE_PATH && DevicePathSubType(dp) == MEDIA_FILEPATH_DP) break; dp = NextDevicePathNode(dp); ++count; } SetDevicePathEndNode(dp); if (debugging) print(L"Device Path prepared: %s\n", DevicePathToStr(parent_dp)); for (i = 0; i < n_handles; i++) { EFI_DEVICE_PATH *path; rc = uefi_call_wrapper(BS->HandleProtocol, 3, devices[i], &dpp, (void **)&path); if (EFI_ERROR(rc)) continue; if (debugging) print(L"Device supporting SFSP: %s\n", DevicePathToStr(path)); rc = EFI_UNSUPPORTED; while (!IsDevicePathEnd(path)) { if (debugging) print(L"Comparing: %s and %s\n", DevicePathToStr(parent_dp), DevicePathToStr(path)); if (LibMatchDevicePaths(path, parent_dp) == TRUE) { *fh = devices[i]; for (i = 0; i < count; i++) *file_dp = NextDevicePathNode(*file_dp); rc = EFI_SUCCESS; if (debugging) print(L"Match up! Returning %s\n", DevicePathToStr(*file_dp)); goto out; } path = NextDevicePathNode(path); } } out: if (!EFI_ERROR(rc)) print(L"File %s searched\n", DevicePathToStr(*file_dp)); uefi_call_wrapper(BS->FreePool, 1, devices); return rc; } static EFI_STATUS open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh) { EFI_DEVICE_PATH *file_dp = dp; EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_FILE_HANDLE device; EFI_FILE_IO_INTERFACE *drive; EFI_FILE_HANDLE root; EFI_STATUS rc; rc = uefi_call_wrapper(BS->LocateDevicePath, 3, &sfsp, &file_dp, (EFI_HANDLE *)&device); if (EFI_ERROR(rc)) { rc = search_file(&file_dp, &device); if (EFI_ERROR(rc)) { print(L"Could not locate device handle: %r\n", rc); return rc; } } if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH || DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) { print(L"Could not find appropriate device.\n"); return EFI_UNSUPPORTED; } UINT16 sz16; UINTN sz; CopyMem(&sz16, &file_dp->Length[0], sizeof(sz16)); sz = sz16; sz -= 4; if (sz <= 6 || sz % 2 != 0) { print(L"Invalid file device path.\n"); return EFI_INVALID_PARAMETER; } sz /= sizeof (CHAR16); /* * check against some arbitrary limit to avoid having a stack * overflow here. */ if (sz > 1024) { print(L"Invalid file device path.\n"); return EFI_INVALID_PARAMETER; } CHAR16 filename[sz+1]; CopyMem(filename, (UINT8 *)file_dp + 4, sz * sizeof (CHAR16)); filename[sz] = L'\0'; rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, &sfsp, (void **)&drive); if (EFI_ERROR(rc)) { print(L"Could not open device interface: %r.\n", rc); return rc; } dprint(L"Found device\n"); rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); if (EFI_ERROR(rc)) { print(L"Could not open volume: %r.\n", rc); return rc; } dprint(L"Found volume\n"); rc = uefi_call_wrapper(root->Open, 5, root, fh, filename, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(rc)) { print(L"Could not open file \"%s\": %r.\n", filename, rc); return rc; } dprint(L"Found file\n"); return EFI_SUCCESS; } static EFI_STATUS delete_boot_order(CHAR16 *name, EFI_GUID guid) { UINTN i; UINT16 boot_num; EFI_STATUS rc; UINTN info_size = 0; UINT32 attributes = 0; void *info_ptr = NULL; UINT16 *new_info_ptr = NULL; BOOLEAN num_found = FALSE; UINTN new_list_num = 0; /* get boot hex number */ boot_num = xtoi((CHAR16 *)((UINT8 *)name + sizeof(L"Boot"))); rc = read_variable(L"BootOrder", guid, &info_ptr, &info_size, &attributes); if (EFI_ERROR(rc)) return rc; new_info_ptr = AllocatePool(info_size); if (!new_info_ptr) { print(L"Tried to allocate %d\n", info_size); print(L"Could not allocate memory.\n"); FreePool(info_ptr); return EFI_OUT_OF_RESOURCES; } for (i = 0; i < (info_size / sizeof(UINT16)) ; i++) { if (((UINT16 *)info_ptr)[i] != boot_num) { new_info_ptr[i] = ((UINT16 *)info_ptr)[i]; new_list_num++; } else { num_found = TRUE; } } /* if not in the BootOrder list, do not update BootOrder */ if (!num_found) { rc = EFI_SUCCESS; goto out; } rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid, attributes, new_list_num * sizeof(UINT16), new_info_ptr); if (EFI_ERROR(rc)) { print(L"Could not update variable status for \"%s\": %r\n", name, rc); goto out; } out: FreePool(info_ptr); FreePool(new_info_ptr); return rc; } static EFI_STATUS delete_boot_entry(void) { EFI_STATUS rc; UINTN variable_name_allocation = GNVN_BUF_SIZE; UINTN variable_name_size = 0; CHAR16 *variable_name; EFI_GUID vendor_guid = empty_guid; UINTN mult_res; EFI_STATUS ret = EFI_OUT_OF_RESOURCES; variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2); if (!variable_name) { print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2); print(L"Could not allocate memory.\n"); return EFI_OUT_OF_RESOURCES; } while (1) { variable_name_size = variable_name_allocation; rc = uefi_call_wrapper(RT->GetNextVariableName, 3, &variable_name_size, variable_name, &vendor_guid); if (rc == EFI_BUFFER_TOO_SMALL) { UINTN new_allocation; CHAR16 *new_name; new_allocation = variable_name_size; if (uintn_mult(new_allocation, 2, &mult_res)) { print(L"%d * 2 would overflow size\n", new_allocation); ret = EFI_OUT_OF_RESOURCES; goto err; } new_name = AllocatePool(new_allocation * 2); if (!new_name) { print(L"Tried to allocate %d\n", new_allocation * 2); print(L"Could not allocate memory.\n"); ret = EFI_OUT_OF_RESOURCES; goto err; } CopyMem(new_name, variable_name, variable_name_allocation); variable_name_allocation = new_allocation; FreePool(variable_name); variable_name = new_name; continue; } else if (rc == EFI_NOT_FOUND) { break; } else if (EFI_ERROR(rc)) { print(L"Could not get variable name: %r\n", rc); ret = rc; goto err; } /* check if the variable name is Boot#### */ UINTN vns = StrLen(variable_name); if (!guid_cmp(&vendor_guid, &global_variable_guid) && vns == 8 && CompareMem(variable_name, L"Boot", 8) == 0) { UINTN info_size = 0; UINT32 attributes = 0; void *info_ptr = NULL; CHAR16 *load_op_description = NULL; CHAR16 target[] = L"Linux-Firmware-Updater"; rc = read_variable(variable_name, vendor_guid, &info_ptr, &info_size, &attributes); if (EFI_ERROR(rc)) { ret = rc; goto err; } /* * check if the boot path created by fwupdate, * check with EFI_LOAD_OPTION decription */ load_op_description = (CHAR16 *) ((UINT8 *)info_ptr + sizeof(UINT32) + sizeof(UINT16)); if (CompareMem(load_op_description, target, sizeof(target) - 2) == 0) { /* delete the boot path from BootOrder list */ rc = delete_boot_order(variable_name, vendor_guid); if (EFI_ERROR(rc)) { print(L"Failed to delete the Linux-Firmware-Updater boot entry from BootOrder.\n"); FreePool(info_ptr); ret = rc; goto err; } rc = delete_variable(variable_name, vendor_guid, attributes); if (EFI_ERROR(rc)) { print(L"Failed to delete the Linux-Firmware-Updater boot entry.\n"); FreePool(info_ptr); ret = rc; goto err; } FreePool(info_ptr); break; } FreePool(info_ptr); } } ret = EFI_SUCCESS; err: FreePool(variable_name); return ret; } static EFI_STATUS get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image) { EFI_HANDLE *handles, gop_handle; UINTN num_handles, i; EFI_STATUS status; EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; void *iface; status = LibLocateHandle(ByProtocol, &gop_guid, NULL, &num_handles, &handles); if (EFI_ERROR(status)) return status; if (!handles || num_handles == 0) return EFI_UNSUPPORTED; for (i = 0; i < num_handles; i++) { gop_handle = handles[i]; status = uefi_call_wrapper(BS->OpenProtocol, 6, gop_handle, &gop_guid, &iface, loaded_image, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) continue; gop = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)iface; *mode = gop->Mode->Mode; return EFI_SUCCESS; } return EFI_UNSUPPORTED; } static UINT32 csum(UINT8 *buf, UINTN size) { UINT32 sum = 0; UINTN i; dprint(L"checksumming %d bytes at 0x%08x\n", size, buf); for (i = 0; i < size; i++) { sum += buf[i]; if (debugging) Print(L"\rpos:%08lx csum:%d", buf+i, sum); } if (debugging) Print(L"\n"); return sum; } static EFI_STATUS do_ux_csum(EFI_HANDLE loaded_image, UINT8 *buf, UINTN size) { ux_capsule_header_t *payload_hdr; EFI_CAPSULE_HEADER *capsule; EFI_STATUS rc; UINTN sum = 0; UINT8 *current = buf; UINTN left = size; if (size < sizeof(*capsule)) { dprint(L"Invalid capsule size %d\n", size); return EFI_INVALID_PARAMETER; } capsule = (EFI_CAPSULE_HEADER *)buf; if (debugging) hexdump(buf, size <= 0x40 ? size : 0x40); dprint(L"size: %d\n", size); dprint(L"&HeaderSize: 0x%08lx\n", &capsule->HeaderSize); dprint(L"HeaderSize: %d\n", capsule->HeaderSize); dprint(L"&CapsuleImageSize: 0x%08lx\n", &capsule->CapsuleImageSize); dprint(L"CapsuleImageSize: %d\n", capsule->CapsuleImageSize); if (size < capsule->HeaderSize) { dprint(L"Invalid capsule header size %d\n", size); return EFI_INVALID_PARAMETER; } sum += csum(current, capsule->HeaderSize); current += capsule->HeaderSize; left -= capsule->HeaderSize; payload_hdr = (ux_capsule_header_t *)(buf) + capsule->HeaderSize; dprint(L"&PayloadHeader: 0x%08lx\n", payload_hdr); dprint(L"PayloadHeader Size: %d\n", sizeof (*payload_hdr)); rc = get_gop_mode(&payload_hdr->mode, loaded_image); if (EFI_ERROR(rc)) return EFI_UNSUPPORTED; payload_hdr->checksum = 0; sum += csum(current, sizeof(*payload_hdr)); current += sizeof(*payload_hdr); left -= sizeof(*payload_hdr); sum += csum(current, left); dprint(L"sum is 0x%02hhx; setting ->checksum to 0x%02hhx\n", sum & 0xff, (uint8_t)((int8_t)(0 - (sum & 0xff)))); payload_hdr->checksum = (uint8_t)((int8_t)(0 - sum)); dprint(L"checksum is set...\n"); return EFI_SUCCESS; } #define is_ux_capsule(guid) (guid_cmp(guid, &ux_capsule_guid) == 0) #define is_fmp_capsule(guid) (guid_cmp(guid, &fmp_capsule_guid) == 0) static EFI_STATUS add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out, EFI_HANDLE loaded_image) { EFI_STATUS rc; EFI_FILE_HANDLE fh = NULL; UINT8 *fbuf = NULL; UINTN fsize = 0; EFI_CAPSULE_HEADER *capsule; UINTN cbd_len; EFI_PHYSICAL_ADDRESS cbd_data; EFI_CAPSULE_HEADER *cap_out; rc = open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh); if (EFI_ERROR(rc)) return rc; rc = read_file(fh, &fbuf, &fsize); if (EFI_ERROR(rc)) return rc; uefi_call_wrapper(fh->Close, 1, fh); dprint(L"Read file; %d bytes\n", fsize); dprint(L"updates guid: %g\n", &update->info->guid); dprint(L"File guid: %g\n", fbuf); /* * See if it has the capsule header, and if not, add one. * * Unfortunately there's not a good way to do this, so we're just * checking if the capsule has the fw_class guid at the right place. */ if ((guid_cmp(&update->info->guid, (efi_guid_t *)fbuf) == 0 || is_fmp_capsule((efi_guid_t *)fbuf)) && /* * We're ignoring things that are 40 bytes here, because that's * the size of the variables used in the test code I wrote for * edk2 - It's basically a capsule header with no payload, so * there's nothing real it can do anyway. * * At some point I'll update that to be slightly different and * take the exception out, but it's not pressing. */ fsize != 40) { dprint(L"Image has capsule image embedded\n"); cbd_len = fsize; cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf; if (!cap_out->Flags && !is_ux_capsule(&update->info->guid)) { #if defined(__aarch64__) cap_out->Flags |= update->info->capsule_flags; #else cap_out->Flags |= update->info->capsule_flags | CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET; #endif } } else { dprint(L"Image does not have embedded header\n"); dprint(L"Allocating %d for capsule header.\n", sizeof (*capsule)+fsize); rc = allocate((void **)&capsule, sizeof (*capsule) + fsize); if (EFI_ERROR(rc)) { print(L"Tried to allocate %d\n", sizeof (*capsule) + fsize); print(L"Could not allocate space for update: %r.\n", rc); return EFI_OUT_OF_RESOURCES; } capsule->CapsuleGuid = update->info->guid; capsule->HeaderSize = sizeof (*capsule); if (!is_ux_capsule(&update->info->guid)) { #if defined(__aarch64__) capsule->Flags |= update->info->capsule_flags; #else capsule->Flags = update->info->capsule_flags | CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET; #endif } capsule->CapsuleImageSize = fsize + sizeof (*capsule); UINT8 *buffer = (UINT8 *)capsule + capsule->HeaderSize; CopyMem(buffer, fbuf, fsize); cbd_len = capsule->CapsuleImageSize; cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)capsule; cap_out = capsule; free(fbuf, fsize); } if (is_ux_capsule(&update->info->guid)) { dprint(L"Checksumming ux capsule\n"); rc = do_ux_csum(loaded_image, (UINT8 *)capsule, cbd_len); if (EFI_ERROR(rc)) return EFI_UNSUPPORTED; } cbd_out->Length = cbd_len; cbd_out->Union.DataBlock = cbd_data; *capsule_out = cap_out; return EFI_SUCCESS; } static EFI_STATUS apply_capsules(EFI_CAPSULE_HEADER **capsules, EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd, UINTN num_updates, EFI_RESET_TYPE *reset) { UINT64 max_capsule_size; EFI_STATUS rc; rc = delete_boot_entry(); if (EFI_ERROR(rc)) { /* * Print out deleting boot entry error, but still try to apply * capsule. */ dprint(L"Could not delete boot entry: %r\n", __FILE__, __func__, __LINE__, rc); } rc = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules, num_updates, &max_capsule_size, reset); dprint(L"QueryCapsuleCapabilities: %r max: %ld reset:%d\n", rc, max_capsule_size, *reset); dprint(L"Capsules: %d\n", num_updates); uefi_call_wrapper(BS->Stall, 1, 1 * SECONDS); rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates, (EFI_PHYSICAL_ADDRESS)(UINTN)cbd); if (EFI_ERROR(rc)) { print(L"Could not apply capsule update: %r\n", rc); return rc; } return EFI_SUCCESS; } static EFI_STATUS set_statuses(UINTN n_updates, update_table **updates) { EFI_STATUS rc; for (UINTN i = 0; i < n_updates; i++) { rc = uefi_call_wrapper(RT->SetVariable, 5, updates[i]->name, &fwupdate_guid, updates[i]->attributes, updates[i]->size, updates[i]->info); if (EFI_ERROR(rc)) { print(L"Coould not update variable status for \"%s\": %r\n", updates[i]->name, rc); return rc; } } return EFI_SUCCESS; } EFI_GUID SHIM_LOCK_GUID = {0x605dab50,0xe046,0x4300,{0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23}}; static void __attribute__((__optimize__("0"))) debug_hook(void) { EFI_GUID guid = SHIM_LOCK_GUID; UINTN data = 0; UINTN data_size = 1; EFI_STATUS efi_status; UINT32 attributes; register volatile int x = 0; extern char _text UNUSED, _data UNUSED; /* * If SHIM_DEBUG is set, we're going to assume shim has done whatever * is needed to get a debugger attached, and we just need to explain * who and where we are, and also enable our debugging output. */ efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG", &guid, &attributes, &data_size, &data); if (EFI_ERROR(efi_status) || data != 1) { efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"FWUPDATE_VERBOSE", &fwupdate_guid, &attributes, &data_size, &data); if (EFI_ERROR(efi_status) || data != 1) { return; } debugging = 1; return; } debugging = 1; if (x) return; x = 1; print(L"add-symbol-file "DEBUGDIR L"fwupdate.efi.debug %p -s .data %p\n", &_text, &_data); } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { EFI_STATUS rc; update_table **updates = NULL; UINTN n_updates = 0; EFI_RESET_TYPE reset_type = EfiResetWarm; InitializeLib(image, systab); /* * if SHIM_DEBUG is set, print info for our attached debugger. */ debug_hook(); /* * Basically the workflow here is: * 1) find and validate any update state variables with the right GUID * 2) allocate our capsule data structures and add the capsules * #1 described * 3) update status variables * 4) apply the capsule updates * 5) reboot */ /* * Step 1: find and validate update state variables */ /* XXX TODO: * 1) survey the reset types first, and separate into groups * according to them * 2) if there's more than one, mirror BootCurrent back into BootNext * so we can do multiple runs * 3) only select the ones from one type for the first go */ rc = find_updates(&n_updates, &updates); if (EFI_ERROR(rc)) { print(L"fwupdate: Could not find updates: %r\n", rc); return rc; } if (n_updates == 0) { print(L"fwupdate: No updates to process. Called in error?\n"); return EFI_INVALID_PARAMETER; } /* * Step 2: Build our data structure and add the capsules to it. */ EFI_CAPSULE_HEADER *capsules[n_updates + 1]; EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data; UINTN i, j; rc = allocate((void **)&cbd_data, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); if (EFI_ERROR(rc)) { print(L"Tried to allocate %d\n", sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); print(L"fwupdate: Could not allocate memory: %r.\n",rc); return rc; } for (i = 0, j = 0; i < n_updates; i++) { dprint(L"Adding new capsule\n"); rc = add_capsule(updates[i], &capsules[j], &cbd_data[j], image); if (EFI_ERROR(rc)) { if (rc == EFI_UNSUPPORTED && is_ux_capsule(&updates[i]->info->guid)) continue; dprint(L"fwupdate: Could not build update list: %r\n", rc); return rc; } j++; } n_updates = j; dprint(L"n_updates: %d\n", n_updates); cbd_data[i].Length = 0; cbd_data[i].Union.ContinuationPointer = 0; /* * Step 3: update the state variables. */ rc = set_statuses(n_updates, updates); if (EFI_ERROR(rc)) { print(L"fwupdate: Could not set update status: %r\n", rc); return rc; } /* * Step 4: apply the capsules. */ rc = apply_capsules(capsules, cbd_data, n_updates, &reset_type); if (EFI_ERROR(rc)) { print(L"fwupdate: Could not apply capsules: %r\n", rc); return rc; } /* * Step 5: if #4 didn't reboot us, do it manually. */ dprint(L"fwupdate: Reset System\n"); if (debugging) uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS); uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS); uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS, 0, NULL); return EFI_SUCCESS; } fwupdate-12/efi/hexdump.h000066400000000000000000000040131331522106300154750ustar00rootroot00000000000000#ifndef STATIC_HEXDUMP_H #define STATIC_HEXDUMP_H static int __attribute__((__unused__)) isprint(char c) { if (c < 0x20) return 0; if (c > 0x7e) return 0; return 1; } static UINTN __attribute__((__unused__)) format_hex(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; CHAR16 hexchars[] = L"0123456789abcdef"; int offset = 0; unsigned int i; unsigned int j; for (i = 0; i < sz; i++) { buf[offset++] = L' '; buf[offset++] = L' '; buf[offset++] = L' '; if (i == 7) buf[offset++] = L' '; } for (j = sz; j < 16 && j < size; j++) { UINT8 d = data[j-sz]; buf[offset++] = hexchars[(d & 0xf0) >> 4]; buf[offset++] = hexchars[(d & 0x0f)]; if (j != 15) buf[offset++] = L' '; if (j == 7) buf[offset++] = L' '; } for (i = j; i < 16; i++) { buf[offset++] = L' '; buf[offset++] = L' '; if (i != 15) buf[offset++] = L' '; if (i == 7) buf[offset++] = L' '; } buf[offset] = L'\0'; return j - sz; } static void __attribute__((__unused__)) format_text(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; int offset = 0; unsigned int i; unsigned int j; for (i = 0; i < sz; i++) buf[offset++] = L' '; buf[offset++] = L'|'; for (j = sz; j < 16 && j < size; j++) { if (isprint(data[j-sz])) buf[offset++] = data[j-sz]; else buf[offset++] = L'.'; } buf[offset++] = L'|'; for (i = j; i < 16; i++) buf[offset++] = L' '; buf[offset] = L'\0'; } static void __attribute__((__unused__)) hexdump(UINT8 *data, UINTN size) { UINTN display_offset = (UINTN)data & 0xffffffff; UINTN offset = 0; //Print(L"hexdump: data=0x%016x size=0x%x\n", data, size); while (offset < size) { CHAR16 hexbuf[49]; CHAR16 txtbuf[19]; UINTN sz; sz = format_hex(data+offset, size-offset, hexbuf); if (sz == 0) return; uefi_call_wrapper(BS->Stall, 1, 200000); format_text(data+offset, size-offset, txtbuf); Print(L"%08x %s %s\n", display_offset, hexbuf, txtbuf); uefi_call_wrapper(BS->Stall, 1, 200000); display_offset += sz; offset += sz; } } #endif fwupdate-12/efi/mkvar.c000066400000000000000000000021271331522106300151420ustar00rootroot00000000000000#include #include #define INSYDE 1 struct fwupdate_entry { EFI_GUID guid; UINT32 version; UINT32 flags; CHAR16 path[40]; }; EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { EFI_GUID fwupdate_guid = {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; #if INSYDE == 1 EFI_GUID fw_guid = {0xffd4675e, 0xff47, 0x46d9,{0xac,0x24,0x8b,0x33,0x1f,0x93,0x77,0x37}}; #else EFI_GUID fw_guid = {0x819b858e,0xc52c,0x402f,{0x80,0xe1,0x5b,0x31,0x1b,0x6c,0x19,0x59}}; #endif struct fwupdate_entry fwue = { .guid = fw_guid, .version = 1413742592, #if INSYDE == 1 .flags = (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET), .path = L"\\isflash.bin", #else .flags = 0x1, .path = L"\\UEFIDevKit_S1200RP_vB2\\SDV_RP_B2_debug.cap", #endif }; InitializeLib(image, systab); EFI_STATUS rc = uefi_call_wrapper(RT->SetVariable, 5, L"FwUpdates", &fwupdate_guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (fwue), &fwue); Print(L"SetVariable: %r\n", rc); return 0; } fwupdate-12/efi/mkvar2.c000066400000000000000000000035541331522106300152310ustar00rootroot00000000000000#include #include #define efidp_header EFI_DEVICE_PATH #define efi_guid_t EFI_GUID #include "fwup-efi.h" typedef struct esre { EFI_GUID fw_class; UINT32 fw_type; UINT32 fw_version; UINT32 lowest_supported_fw_version; UINT32 capsule_flags; UINT32 last_attempt_version; UINT32 last_attempt_status; } __attribute__ ((__packed__)) esre_t; #define guid0 {0x0712233d,0xfe15,0x434c,{0xbf,0x4d,0xa3,0x4a,0x05,0x03,0x14,0x4a}} #define guid1 {{0xeac48586,0xebf7,0x4901,0xb2,0x32,0x0b,0x29,0xe9,0x9a,0xe6,0xa9}} uint8_t devicepath[] = "\x02\x01\x0c\x00\xd0\x41\x03\x0a\x00\x00\x00\x00" "\x01\x01\x06\x00\x01\x01\x03\x01\x08\x00\x00\x00\x00\x00\x04\x01" "\x2a\x00\x01\x00\x00\x00\xa1\x07\x00\x00\x00\x00\x00\x00\x3e\x20" "\x00\x00\x00\x00\x00\x00\xe7\x2f\x41\x52\xa7\x80\x72\x4c\x8b\x0b" "\x02\x9e\xab\x6f\x0c\x2b\x02\x02\x04\x04\x1a\x00\x5c\x00\x65\x00" "\x73\x00\x72\x00\x65\x00\x30\x00\x2e\x00\x63\x00\x61\x00\x70\x00" "\x00\x00\x7f\xff\x04\x00"; EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { EFI_GUID fwupdate_guid = {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; UINT8 buf[EFI_FIELD_OFFSET(update_info, dp)+sizeof(devicepath)-1]; update_info *info = (update_info *)buf; efi_guid_t guid = guid0; info->update_info_version = UPDATE_INFO_VERSION; info->capsule_flags = 0x80f1; info->hw_inst = 0; info->status = FWUPDATE_ATTEMPT_UPDATE; InitializeLib(image, systab); ZeroMem(&info->time_attempted, sizeof (info->time_attempted)); CopyMem(info->dp_buf, devicepath, sizeof (devicepath)-1); CopyMem(&info->guid, &guid, sizeof (guid)); EFI_STATUS rc = uefi_call_wrapper(RT->SetVariable, 5, L"FwUpdates", &fwupdate_guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (buf), buf); Print(L"SetVariable: %r\n", rc); return 0; } fwupdate-12/fwupdate.spec.in000066400000000000000000000164761331522106300162270ustar00rootroot00000000000000%global efivar_version 32-1 %global efibootmgr_version 13-1 %global gnu_efi_version 3.0.5-11 %undefine _debuginfo_subpackages Name: fwupdate Version: @@VERSION@@ Release: 1%{?dist} Summary: Tools to manage UEFI firmware updates License: GPLv2+ URL: https://github.com/rhinstaller/fwupdate Requires: %{name}-libs%{?_isa} = %{version}-%{release} BuildRequires: efivar-devel >= %{efivar_version} BuildRequires: gnu-efi >= %{gnu_efi_version} BuildRequires: gnu-efi-devel >= %{gnu_efi_version} BuildRequires: pesign BuildRequires: elfutils popt-devel git gettext pkgconfig BuildRequires: systemd BuildRequires: libabigail %ifarch x86_64 BuildRequires: libsmbios-devel %endif ExclusiveArch: x86_64 aarch64 Source0: https://github.com/rhinstaller/fwupdate/releases/download/%{name}-%{version}/%{name}-%{version}.tar.bz2 Source1: find-debuginfo-efi.sh %global __os_install_post %{expand:\ %{?__debug_package:%{__debug_install_post}} \ %{SOURCE1} \ %{__arch_install_post} \ %{__os_install_post} \ %{nil}}%{nil} %ifarch x86_64 %global efiarch x64 %global efialtarch ia32 %endif %ifarch aarch64 %global efiarch aa64 %endif # Figure out the right file path to use %global efidir %(eval echo $(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/')) %description fwupdate provides a simple command line interface to the UEFI firmware updates. %package libs Summary: Library to manage UEFI firmware updates %ifnarch %{ix86} Requires: shim %endif Requires: %{name}-efi = %{version}-%{release} %description libs Library to allow for the simple manipulation of UEFI firmware updates. %package devel Summary: Development headers for libfwup Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: efivar-devel >= %{efivar_version} %description devel development headers required to use libfwup. %package efi Summary: UEFI binaries used by libfwup Requires: %{name}-libs = %{version}-%{release} %description efi UEFI binaries used by libfwup. %package efi-debuginfo Summary: debuginfo for UEFI binaries used by libfwup Requires: %{name}-efi = %{version}-%{release} AutoReq: 0 AutoProv: 1 %description efi-debuginfo debuginfo for UEFI binaries used by libfwup. %prep %setup -q -n %{name}-%{version} git init git config user.email "%{name}-owner@fedoraproject.org" git config user.name "Fedora Ninjas" git add . mkdir build-%{efiarch} %ifarch x86_64 mkdir build-%{efialtarch} %endif git commit -a -q -m "%{version} baseline." git am %{patches} - 9-1 - Update to fwupdate 9 - gcc 7 fixes - Ensure BootNext entries are in BootOrder to work around some broken firmwares. - Minor bug fixes - Support for detecting that an admin password is set when trying to enable firmware features. * Fri Aug 19 2016 Peter Jones - 8-1 - Update to fwupdate 8 - Fix some i686 build errors - Be less stupid about SONAMEs so in the future we'll only have to rebuild dependent things on actual ABI changes. - Only depend on libsmbios on x86, for now, because it hasn't been ported to Aarch64. * Tue Aug 16 2016 Peter Jones - 7-1 - Update to fwupdate 7. - Minor bug fixes. * Tue Aug 16 2016 Peter Jones - 6-1 - Update to 0.6 - lots of build fixes for newer compilers and such - Use libsmbios on some systems to enable firmware updates (Mario Limonciello) - Use the correct reset type from the QueryCapsuleInfo data - Lots of fixes from auditing - Use efivar's error reporting infrastructure * Tue Jun 02 2015 Peter Jones - 0.4-1 - Update to 0.4 - Set DESTDIR so it's more consistently respected - Always use upper case for Boot#### names. - Create abbreviated device paths for our BootNext entry. - Make subdir Makefiles get the version right. - Fix ucs2len() to handle max=-1 correctly. - Compare the right blobs when we're searching old boot entries. - Fix .efi generation on non-x86 platforms. - Use a relative path for fwupdate.efi when launched from shim. - Show fewer debugging messages. - Set BootNext when we find an old Boot#### variable as well. - Add fwup_get_fw_type(). * Thu May 28 2015 Peter Jones - 0.3-1 - Here we go again. fwupdate-12/include/000077500000000000000000000000001331522106300145345ustar00rootroot00000000000000fwupdate-12/include/Makefile000066400000000000000000000007271331522106300162020ustar00rootroot00000000000000ifneq ($(origin TOPDIR),undefined) TOP := $(abspath $(TOPDIR)) else TOP := $(abspath ..) endif include $(TOP)/Make.defaults include $(TOP)/Make.rules VPATH = $(TOP)/include all clean dep abidw abicheck : install : $(INSTALL) -d -m 755 $(DESTDIR)$(DEBUGSOURCE)/fwupdate-$(VERSION)$(DASHRELEASE)$(DOTARCH)/include $(foreach src,$(wildcard *.[chS]),$(INSTALL) -m 644 $(src) $(DESTDIR)$(DEBUGSOURCE)/fwupdate-$(VERSION)$(DASHRELEASE)$(DOTARCH)/include/$(src) ;) fwupdate-12/include/fwup-efi.h000066400000000000000000000020421331522106300164250ustar00rootroot00000000000000/* * fwup-efi.h: shared structures between the linux frontend and the efi backend. * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #ifndef _FWUP_EFI_H #define _FWUP_EFI_H #define FWUPDATE_ATTEMPT_UPDATE 0x00000001 #define FWUPDATE_ATTEMPTED 0x00000002 #define UPDATE_INFO_VERSION 7 #ifdef _EFI_INCLUDE_ #define efidp_header EFI_DEVICE_PATH #define efi_guid_t EFI_GUID #endif /* _EFI_INCLUDE_ */ typedef struct { uint8_t version; uint8_t checksum; uint8_t image_type; uint8_t reserved; uint32_t mode; uint32_t x_offset; uint32_t y_offset; } ux_capsule_header_t; typedef struct update_info_s { uint32_t update_info_version; /* stuff we need to apply an update */ efi_guid_t guid; uint32_t capsule_flags; uint64_t hw_inst; EFI_TIME time_attempted; /* our metadata */ uint32_t status; /* variadic device path */ union { efidp_header *dp_ptr; efidp_header dp; uint8_t dp_buf[0]; }; } __attribute__((__packed__)) update_info; #endif /* _FWUP_EFI_H */ fwupdate-12/linux/000077500000000000000000000000001331522106300142505ustar00rootroot00000000000000fwupdate-12/linux/.gitignore000066400000000000000000000001001331522106300162270ustar00rootroot00000000000000fwupdate lib*.so.* tester *.po lib*.map cleanup.service cleanup fwupdate-12/linux/Makefile000066400000000000000000000120341331522106300157100ustar00rootroot00000000000000default : all ifneq ($(origin TOPDIR),undefined) TOP := $(abspath $(TOPDIR)) else TOP := $(abspath ..) endif include $(TOP)/Make.defaults include $(TOP)/Make.rules VPATH = $(TOP)/linux LIB_LIBS= pthread BIN_LIBS= popt pthread PKLIBS = efivar efiboot CFLAGS ?= -g3 -Og -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 \ -grecord-gcc-switches PJONES = BIN_CCLDFLAGS = $(foreach lib,$(BIN_LIBS),-l$(lib)) \ $(foreach pklib,$(PKLIBS), \ $(shell $(PKG_CONFIG) --libs-only-l --libs-only-other $(pklib))) \ $(LDFLAGS) -pie -fPIE -Wl,-z,relro,-z,now -L. LIB_CCLDFLAGS = $(foreach lib,$(LIB_LIBS),-l$(lib)) \ $(foreach pklib,$(PKLIBS), \ $(shell $(PKG_CONFIG) --libs-only-l --libs-only-other $(pklib))) \ $(LDFLAGS) -shared -fPIC -Wl,-z,relro,-z,now HAVE_LIBSMBIOS := $(shell $(PKG_CONFIG) --exists libsmbios_c && \ echo yes || echo no) ifeq ($(HAVE_LIBSMBIOS),yes) PKLIBS += libsmbios_c CFLAGS += -DFWUPDATE_HAVE_LIBSMBIOS__ endif BUILDFLAGS := $(CFLAGS) -Wall -Wextra -Werror -Wno-error=cpp \ -Wno-unused-result -Wno-unused-function \ -Wsign-compare -Werror=sign-compare \ -Wno-error=missing-field-initializers -Wno-missing-field-initializers \ -fshort-wchar --std=gnu11 \ -DLOCALEDIR=\"$(localedir)\" -D_GNU_SOURCE \ -DFWUP_EFI_DIR_NAME=\"$(EFIDIR)\" \ -DFWUP_ESP_MOUNTPOINT=\"$(ESPMOUNTPOINT)\" \ -I include -I $(TOP)/linux/include/ -iquote $(TOP)/include/ \ $(foreach pklib,$(PKLIBS), $(shell $(PKG_CONFIG) --cflags $(pklib))) \ $(PJONES) BINTARGETS=fwupdate INCTARGETS=include/fwup-version.h $(VPATH)/include/fwup.h LIBTARGETS=libfwup.so PCTARGETS=fwup.pc POTARGETS = fwupdate.po libfwup.po TARGETS=$(BINTARGETS) $(INCTARGETS) $(foreach x,$(LIBTARGETS),$(patsubst %.so,%.map,$(x)) $(x).1.$(VERSION) $(x).1 $(x)) $(PCTARGETS) $(POTARGETS) cleanup.service cleanup .SUFFIXES: all : $(TARGETS) fwupdate : libfwup.so include-dir : if [ ! -d include ]; then \ $(INSTALL) -d -m 755 include ; \ fi fwupdate.c : | include/fwup.h libfwup.c : | include/fwup.h include/fwup.h : | include/fwup-version.h include/fwup-version.h : | include-dir abicheck : | clean $(LIBTARGETS) abicheck : $(patsubst %.so,%.abicheck,$(LIBTARGETS)) abiclean : @rm -vf $(patsubst %.so,%.abixml,$(LIBTARGETS)) abixml : | $(LIBTARGETS) abixml : $(patsubst %.so,%.abixml,$(LIBTARGETS)) abiupdate : abixml git add $(patsubst %.so,%.abixml,$(LIBTARGETS)) % : %.o $(CC) $(BUILDFLAGS) -o $@ $(patsubst lib%.so,-l%,$^) $(BIN_CCLDFLAGS) %.so.1.$(VERSION) : %.o | %.map $(CC) $(BUILDFLAGS) \ -Wl,-soname,$(patsubst %.o,%.so.1,$<) \ -Wl,--version-script=$(patsubst %.o,%.map,$<) \ -o $@ $< $(LIB_CCLDFLAGS) %.so.1 : %.so.1.$(VERSION) ln -sf $< $@ %.so : %.so.1.$(VERSION) ln -sf $< $@ %.po : %.c $(XGETTEXT) -a --package-name=$(patsubst %.po,%,$@) --package-version=$(VERSION) -o $@ $^ %.o : %.c $(CC) $(BUILDFLAGS) -fPIC -c -o $@ $^ % : %.in sed -e "s,@@FWUP_VERSION@@,$(VERSION),g" \ -e "s,@@FWUP_MAJOR_VERSION@@,$(MAJOR_VERSION),g" \ -e "s,@@FWUP_MINOR_VERSION@@,$(MINOR_VERSION),g" \ -e "s,@@PREFIX@@,$(prefix),g" \ -e "s,@@EXEC_PREFIX@@,$(exec_prefix),g" \ -e "s,@@SHAREDSTATEDIR@@,$(sharedstatedir),g" \ -e "s,@@ESPMOUNTPOINT@@,$(ESPMOUNTPOINT),g" \ -e "s,@@EFIDIR@@,$(EFIDIR),g" \ -e "s,@@LIBDIR@@,$(libdir),g" \ -e "s,@@LIBEXECDIR@@,$(libexecdir),g" \ -e "s,@@INCLUDEDIR@@,$(includedir),g" \ $< > $@ tester : tester.c $(CC) -Wall -Werror -ggdb -L. -I./include $(shell $(PKG_CONFIG) --cflags efivar) \ $(shell $(PKG_CONFIG) --libs efivar) -lfwup -o $@ $^ test : tester LD_LIBRARY_PATH=$(shell pwd) ./tester clean : @rm -vf $(BINTARGETS) $(LIBTARGETS) $(PCTARGETS) $(POTARGETS) *.o @rm -vf libfwup.so* *.map @rm -vf cleanup.service cleanup @rm -vf include/fwup-version.h install : check_efidir_error all $(INSTALL) -d -m 755 $(DESTDIR)$(libdir) $(foreach x, $(LIBTARGETS), $(INSTALL) -m 755 $(x).1.$(VERSION) $(DESTDIR)$(libdir);) $(INSTALL) -d -m 755 $(DESTDIR)$(pcdir) $(foreach x, $(PCTARGETS), $(INSTALL) -m 644 $(x) $(DESTDIR)$(pcdir) ;) $(INSTALL) -d -m 755 $(DESTDIR)$(includedir)/ $(INSTALL) -m 644 $(INCTARGETS) $(DESTDIR)$(includedir)/ $(INSTALL) -d -m 755 $(DESTDIR)$(localedir)/en/ $(INSTALL) -m 644 $(POTARGETS) $(DESTDIR)$(localedir)/en/ $(INSTALL) -d -m 755 $(DESTDIR)$(bindir)/ $(foreach x, $(BINTARGETS), $(INSTALL) -m 755 $(x) $(DESTDIR)$(bindir);) $(foreach x, $(wildcard *.so.1.$(VERSION)), \ ln -fs $(x) $(patsubst %.so.1.$(VERSION),%.so.1,$(DESTDIR)$(libdir)/$(x)) ;\ ln -fs $(x) $(patsubst %.so.1.$(VERSION),%.so,$(DESTDIR)$(libdir)/$(x)) ;\ ) $(INSTALL) -d -m 755 $(DESTDIR)$(sharedstatedir)/fwupdate/ $(INSTALL) -d -m 755 $(DESTDIR)$(libexecdir)/fwupdate/ $(INSTALL) -m 755 cleanup $(DESTDIR)$(libexecdir)/fwupdate/cleanup $(INSTALL) -d -m 755 $(DESTDIR)$(libdatadir)/systemd/system $(INSTALL) -m 644 cleanup.service \ $(DESTDIR)$(libdatadir)/systemd/system/fwupdate-cleanup.service $(INSTALL) -d -m 755 $(DESTDIR)$(datadir)/bash-completion/completions/ $(INSTALL) -m 755 $(VPATH)/bash-completion \ $(DESTDIR)$(datadir)/bash-completion/completions/fwupdate fwupdate-12/linux/abignore000066400000000000000000000004311331522106300157570ustar00rootroot00000000000000[suppress_function] change_kind = added-function soname_regexp = ^libfwup\\.so\\.[[:digit:]]+ symbol_name_regexp = fwup_[[:alnum:]_]+ [suppress_variable] change_kind = added-variable soname_regexp = ^libfwup\\.so\\.[[:digit:]]+ symbol_name_regexp = fwup_[[:alnum:]_]+ fwupdate-12/linux/bash-completion000077500000000000000000000007131331522106300172630ustar00rootroot00000000000000#!/bin/bash _fwupdate() { local cur=${COMP_WORDS[COMP_CWORD]} if [[ "$cur" == -* ]]; then COMPREPLY=( $( compgen -W "-a -l -d --set-debug -D --unset-debug" -- "$cur" ) ) return 0 fi case "${COMP_WORDS[COMP_CWORD-1]}" in -a) COMPREPLY=( $( compgen -W "$(fwupdate -l | cut -d\{ -f2 | cut -d\} -f1)" -- "$cur" ) ) return 0 ;; esac case "${COMP_WORDS[COMP_CWORD-2]}" in -a) _filedir return 0 ;; esac } complete -F _fwupdate fwupdate fwupdate-12/linux/cleanup.in000077500000000000000000000014271331522106300162360ustar00rootroot00000000000000#!/bin/sh set -e if [ -e "@@SHAREDSTATEDIR@@/fwupdate/done" ]; then exit 0 fi efibootmgr -q BOOTNUM=$(efibootmgr | grep "Linux.Firmware.Updater" | sed "s/\ .*//; s/*//; s/Boot//") if [ -n "$BOOTNUM" ]; then for num in $BOOTNUM; do efibootmgr -q -b "$num" -B done fi for x in @@ESPMOUNTPOINT@@/EFI/@@EFIDIR@@/fw/fwupdate-* ; do if [ "${x}" != "@@ESPMOUNTPOINT@@/EFI/@@EFIDIR@@/fw/fwupdate-*" ]; then rm -f "${x}" fi done for x in /sys/firmware/efi/efivars/fwupdate-*-0abba7dc-e516-4167-bbf5-4d9d1c739416 ; do if [ "${x}" != "/sys/firmware/efi/efivars/fwupdate-*-0abba7dc-e516-4167-bbf5-4d9d1c739416" ]; then if lsattr "${x}" 2>/dev/null | cut -d\ -f1 | cut -d- -f5 | grep -q i ; then chattr -i "${x}" fi rm -f "${x}" fi done touch "@@SHAREDSTATEDIR@@/fwupdate/done" fwupdate-12/linux/cleanup.service.in000066400000000000000000000001731331522106300176670ustar00rootroot00000000000000[Unit] Description=fwupdate housekeeping [Service] PrivateTmp=true Type=oneshot ExecStart=@@LIBEXECDIR@@/fwupdate/cleanup fwupdate-12/linux/error.h000066400000000000000000000071701331522106300155570ustar00rootroot00000000000000/* error.h - some error functions to work with efivars error logger. Copyright 2016 Red Hat, 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 */ #ifndef EFIBOOTMGR_ERROR_H__ #define EFIBOOTMGR_ERROR_H__ 1 extern int verbose; static inline void __attribute__((__unused__)) error_reporter(void) { int rc = 1; int saved_errno = errno; for (int i = 0; rc > 0; i++) { char *filename = NULL; char *function = NULL; int line = 0; char *message = NULL; int error = 0; rc = efi_error_get(i, &filename, &function, &line, &message, &error); if (rc < 0) { fprintf(stderr, "error fetching trace value"); exit(1); } if (rc == 0) break; fprintf(stderr, " %s:%d %s(): %s: %s\n", filename, line, function, message, strerror(error)); } errno = saved_errno; } static inline void __attribute__((__unused__)) conditional_error_reporter(int show, int clear) { int saved_errno = errno; fflush(NULL); if (show) { fprintf(stderr, "error trace:\n"); error_reporter(); } if (clear) { errno = 0; efi_error_clear(); } errno = saved_errno; } static inline void __attribute__((__unused__)) cond_error(int test, int eval, const char *fmt, ...) { int saved_errno = errno; if (!test) return; fflush(NULL); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); errno = saved_errno; fprintf(stderr, ": %m\n"); conditional_error_reporter(verbose >= 2, 0); va_end(ap); exit(eval); } static inline void __attribute__((__unused__)) error(int eval, const char *fmt, ...) { int saved_errno = errno; fflush(NULL); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); errno = saved_errno; fprintf(stderr, ": %m\n"); conditional_error_reporter(verbose >= 2, 0); va_end(ap); exit(eval); } static inline void __attribute__((__unused__)) errorx(int eval, const char *fmt, ...) { fflush(NULL); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); conditional_error_reporter(verbose >= 2, 1); va_end(ap); exit(eval); } static inline void __attribute__((__unused__)) cond_warning(int test, const char *fmt, ...) { int saved_errno = errno; if (!test) return; va_list ap; va_start(ap, fmt); vprintf(fmt, ap); errno = saved_errno; printf(": %m\n"); conditional_error_reporter(verbose >= 2, 1); va_end(ap); } static inline void __attribute__((__unused__)) warning(const char *fmt, ...) { int saved_errno = errno; va_list ap; va_start(ap, fmt); vprintf(fmt, ap); errno = saved_errno; printf(": %m\n"); conditional_error_reporter(verbose >= 2, 1); va_end(ap); } static inline void __attribute__((__unused__)) warningx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); printf("\n"); conditional_error_reporter(verbose >= 2, 1); va_end(ap); } #endif /* EFIBOOTMGR_ERROR_H__ */ fwupdate-12/linux/fix_coverity.h000066400000000000000000000053261331522106300171410ustar00rootroot00000000000000/* * fix_coverity.h * Copyright 2017 Peter Jones * * Distributed under terms of the GPLv3 license. */ #ifndef FIX_COVERITY_H #define FIX_COVERITY_H #ifndef __COVERITY_GCC_VERSION_AT_LEAST #define __COVERITY_GCC_VERSION_AT_LEAST(x, y) 0 #define FAKE__COVERITY_GCC_VERSION_AT_LEAST__ #endif /* __COVERITY_GCC_VERSION_AT_LEAST */ /* With gcc 7 on x86_64 (at least), coverity pretends to be GCC but * accidentally doesn't create all of the types GCC would. * * In glibc's headers, bits/floatn.h has: * * #if (defined __x86_64__ \ * ? __GNUC_PREREQ (4, 3) \ * : (defined __GNU__ ? __GNUC_PREREQ (4, 5) : __GNUC_PREREQ (4, 4))) * # define __HAVE_FLOAT128 1 * #else * # define __HAVE_FLOAT128 0 * #endif * * and stdlib.h has: * * #if __HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT) * slash* Likewise for the '_Float128' format *slash * extern _Float128 strtof128 (const char *__restrict __nptr, * char **__restrict __endptr) * __THROW __nonnull ((1)); * #endif * * Which then causes cov-emit to lose its shit: * * "/usr/include/stdlib.h", line 133: error #20: identifier "_Float128" is * undefined * extern _Float128 strtof128 (const char *__restrict __nptr, * ^ * "/usr/include/stdlib.h", line 190: error #20: identifier "_Float128" is * undefined * _Float128 __f) * ^ * "/usr/include/stdlib.h", line 236: error #20: identifier "_Float128" is * undefined * extern _Float128 strtof128_l (const char *__restrict __nptr, * ^ * * And then you'll notice something like this later on: * [WARNING] Emitted 0 C/C++ compilation units (0%) successfully * * 0 C/C++ compilation units (0%) are ready for analysis * For more details, please look at: * /home/pjones/devel/github.com/dbxtool/master/cov-int/build-log.txt * * You would think that if you're writing something that pretends to be * gcc, and you've got a "build a configuration by running shit through gcc * and looking at the output" stage (which they do), you would run "gcc -da * -fdump-tree-all -c -o foo.o foo.c" on an empty file and snarf up all the * types defined in the foo.c.001t.tu output. Apparently, they do not. * * So if we're in that case, just define the type for the thing. */ #ifdef __x86_64__ #if __COVERITY_GCC_VERSION_AT_LEAST(7, 0) typedef float _Float128 __attribute__((__vector_size__(128))); #endif #endif #ifdef FAKE__COVERITY_GCC_VERSION_AT_LEAST__ #undef FAKE__COVERITY_GCC_VERSION_AT_LEAST #undef __COVERITY_GCC_VERSION_AT_LEAST #endif #endif /* !FIX_COVERITY_H */ // vim:fenc=utf-8:tw=75 fwupdate-12/linux/fwup.pc.in000066400000000000000000000003561331522106300161660ustar00rootroot00000000000000prefix=@@PREFIX@@ exec_prefix=@@EXEC_PREFIX@@ libdir=@@LIBDIR@@ includedir=@@INCLUDEDIR@@ Name: fwup Description: Library for deployment of UEFI firmware updates. Version: @@FWUP_VERSION@@ Requires: efivar >= 33 Libs: -L${libdir} -lfwup fwupdate-12/linux/fwupdate.c000066400000000000000000000244751331522106300162470ustar00rootroot00000000000000/* * fwupdate.c - apply firmware updates * * 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 "util.h" #include "error.h" #include "ucs2.h" int verbose = 0; int quiet = 0; static int print_system_resources(void) { fwup_resource_iter *iter; int rc; rc = fwup_resource_iter_create(&iter); if (rc < 0) { if (errno != ENOENT) efi_error(_("Could not create iterator")); return -1; } fwup_resource *re = NULL; while ((rc = fwup_resource_iter_next(iter, &re)) > 0) { efi_guid_t *guid = NULL; char *id_guid = NULL; uint32_t vers; uint32_t lowest; uint32_t type; char *str_type; fwup_get_guid(re, &guid); rc = efi_guid_to_id_guid(guid, &id_guid); if (rc < 0) { efi_error(_("%s failed."), "efi_guid_to_id_guid"); return -1; } fwup_get_fw_version(re, &vers); fwup_get_lowest_supported_fw_version(re, &lowest); fwup_get_fw_type(re, &type); switch (type) { case FWUP_RESOURCE_TYPE_UNKNOWN: str_type = "Unknown"; break; case FWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE: str_type = "System Firmware"; break; case FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE: str_type = "Device Firmware"; break; case FWUP_RESOURCE_TYPE_UEFI_DRIVER: str_type = "UEFI Driver"; break; default: str_type = EMPTY; break; } printf(_("%s type, %s version %u can be updated to any version above %d\n"), str_type , id_guid, vers, lowest-1); free(id_guid); } fwup_resource_iter_destroy(&iter); if (rc < 0) return -1; return 0; } static void set_debug_flag(int8_t set_debug) { int rc; uint8_t *data; size_t size; uint32_t attributes; const char *name = "FWUPDATE_VERBOSE"; efi_guid_t fwupdate_guid = FWUPDATE_GUID; if (set_debug == -1) return; rc = efi_get_variable(fwupdate_guid, name, &data, &size, &attributes); if (rc >= 0) { if (size == 1 && *(int *)data == set_debug) return; efi_del_variable(fwupdate_guid, name); printf(_("Disabled fwupdate debugging\n")); } if (set_debug <= 0) return; attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; rc = efi_set_variable(fwupdate_guid, name, (uint8_t *)&set_debug, sizeof(set_debug), attributes, 0644); if (rc < 0) printf(_("Could not enable fwupdate debugging\n")); else printf(_("Enabled fwupdate debugging\n")); } static void dump_log(void) { int rc; char *utf8 = NULL; size_t size = 0; rc = fwup_get_debug_log(&utf8, &size); if (rc < 0) { if (errno == ENOENT) { printf(_("No debug log found\n")); return; } error(1, _("Could not get debug log")); } printf("%s", utf8); free(utf8); } #define ACTION_APPLY 0x01 #define ACTION_LIST 0x02 #define ACTION_SUPPORTED 0x04 #define ACTION_INFO 0x08 #define ACTION_ENABLE 0x10 #define ACTION_VERSION 0x20 #define ACTION_SHOW_LOG 0x40 int main(int argc, char *argv[]) { int action = 0; int force = 0; int set_debug = -1; int use_existing_media_path = 1; char *esp_path = FWUP_ESP_MOUNTPOINT; const char *guidstr = NULL; const char *filename = NULL; efi_guid_t guid; setlocale(LC_ALL, EMPTY); bindtextdomain("fwupdate", LOCALEDIR); textdomain("fwupdate"); struct poptOption options[] = { {.argInfo = POPT_ARG_INTL_DOMAIN, .arg = "fwupdate" }, {.longName = "apply", .shortName = 'a', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_APPLY, .descrip = _("Apply firmware updates"), .argDescrip = " "}, {.longName = "list", .shortName = 'l', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_LIST, .descrip = _("List supported firmware updates") }, {.longName = "log", .shortName = 'L', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_SHOW_LOG, .descrip = _("Show the debug log from the last attempted update"), }, {.longName = "supported", .shortName = 's', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_SUPPORTED, .descrip = _("Query for firmware update support") }, {.longName = "info", .shortName = 'i', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_INFO, .descrip = _("Show the information of firmware update status")}, {.longName = "enable", .shortName = 'e', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OR, .arg = &action, .val = ACTION_ENABLE, .descrip = _("Enable firmware update support on supported systems (will require a reboot)") }, {.longName = "version", .shortName = '\0', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OPTIONAL, .arg = &action, .val = ACTION_VERSION, .descrip = _("Display version"), }, {.longName = "quiet", .shortName = 'q', .argInfo = POPT_ARG_VAL, .arg = &quiet, .val = 1, .descrip = _("Work quietly") }, {.longName = "force", .shortName = 'f', .argInfo = POPT_ARG_VAL, .arg = &force, .val = 1, .descrip = _("Forces flash even if GUID isn't in ESRT.") }, {.longName = "esp-path", .shortName = 'p', .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, .arg = &esp_path, .val = 0, .descrip = _("Override the default ESP path"), .argDescrip = ""}, {.longName = "dont-use-existing-media-path", .shortName = 'F', .argInfo = POPT_ARG_VAL, .arg = &use_existing_media_path, .val = 0, .descrip = _("Don't reuse the filename for this GUID from previous updates") }, {.longName = "set-debug", .shortName = 'd', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OPTIONAL, .arg = &set_debug, .val = 1, .descrip = _("Set the debugging flag during update"), }, {.longName = "unset-debug", .shortName = 'D', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OPTIONAL, .arg = &set_debug, .val = 0, .descrip = _("Set the debugging flag during update"), }, {.longName = "verbose", .shortName = 'v', .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_OPTIONAL, .arg = &verbose, .val = 2, .descrip = _("Be more verbose on errors"), }, POPT_AUTOALIAS POPT_AUTOHELP POPT_TABLEEND }; poptContext optcon; optcon = poptGetContext("fwupdate", argc, (const char **)argv, options, 0); int rc; rc = poptReadDefaultConfig(optcon, 0); if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) errorx(1, _("%s failed: %s: %s"), "poptReadDefaultConfig", poptBadOption(optcon, 0), poptStrerror(rc)); while ((rc = poptGetNextOpt(optcon)) > 0) ; fwup_set_esp_mountpoint(esp_path); set_debug_flag(set_debug); if (action & ACTION_SHOW_LOG) dump_log(); if (action & ACTION_APPLY) { guidstr = poptGetArg(optcon); if (!guidstr) { warningx(_("missing argument: %s"), "guid"); poptPrintUsage(optcon, stderr, 0); exit(1); } rc = efi_str_to_guid(guidstr, &guid); if (rc < 0) errorx(1, _("Invalid guid: \"%s\""), guidstr); filename = poptGetArg(optcon); if (!filename) { warningx(_("missing argument: %s"), ""); poptPrintUsage(optcon, stderr, 0); exit(1); } } if (rc < -1) errorx(2, _("invalid argument: \"%s\": %s"), poptBadOption(optcon, 0), poptStrerror(rc)); if (poptPeekArg(optcon)) errorx(3, _("invalid argument: \"%s\""), poptPeekArg(optcon)); if (!action && set_debug == -1) { warningx(_("no action specified")); poptPrintUsage(optcon, stderr, 0); exit(4); } poptFreeContext(optcon); if (action & ACTION_SUPPORTED) { rc = fwup_supported(); if (rc == FWUP_SUPPORTED_STATUS_UNSUPPORTED) { qprintf("%s\n", _("Firmware updates are not supported on this machine.")); return 1; } else if (rc == FWUP_SUPPORTED_STATUS_UNLOCKED) { qprintf("%s\n", _("Firmware updates are supported on this machine.")); return 0; } else if (rc == FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK) { qprintf("%s\n%s\n", _("Firmware updates are supported on this machine."), _("Support is currently disabled.")); return 2; } else if (rc == FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK_NEXT_BOOT) { qprintf("%s\n%s\n", _("Firmware updates are supported on this machine."), _("Support will be enabled on the next reboot.")); return 2; } } else if (action & ACTION_LIST) { rc = print_system_resources(); if (rc < 0 && errno != ENOENT) errorx(5, _("Could not list system firmware resources")); return 0; } else if (action & ACTION_APPLY) { fwup_resource_iter *iter = NULL; fwup_resource_iter_create(&iter); fwup_resource *re = NULL; efi_guid_t *tmpguid = NULL; while (!force) { rc = fwup_resource_iter_next(iter, &re); if (rc < 0) error(2, _("Could not iterate resources")); if (rc == 0) break; fwup_get_guid(re, &tmpguid); if (!efi_guid_cmp(tmpguid, &guid)) break; tmpguid = NULL; } if (!tmpguid && force) { rc = fwup_set_guid_forced(iter, &re, &guid, force); if (rc < 0) error(2, _("Error configuring GUID")); tmpguid = &guid; } if (tmpguid) { int fd = open(filename, O_RDONLY); if (fd < 0) error(2, _("could not open \"%s\""), filename); fwup_use_existing_media_path(use_existing_media_path); rc = fwup_set_up_update(re, 0, fd); if (rc < 0) error(2, _("Could not set up firmware update")); fwup_resource_free(re); if (iter) fwup_resource_iter_destroy(&iter); exit(0); } errorx(2, _("firmware resource not found")); } else if (action & ACTION_INFO) { rc = fwup_print_update_info(); if (rc < 0) errorx(6, _("Could not display firmware update status")); return 0; } else if (action & ACTION_ENABLE) { if (geteuid() != 0) { qprintf("%s\n", _("To enable firmware updates, this tool must be launched as root.")); return -1; } rc = fwup_enable_esrt(); if (rc < 1) { qprintf("%s\n", _("Firmware updates can not be enabled on this machine from this tool.")); return 1; } else if (rc == 1) { qprintf("%s\n", _("Firmware updates are already enabled.")); return 1; } else if (rc == 2 || rc == 3) { qprintf("%s\n", _("Firmware updates will be enabled after the system is rebooted.")); return 0; } } else if (action & ACTION_VERSION) { qprintf("fwupdate version: %d\n", LIBFWUP_VERSION); return 0; } return 0; } fwupdate-12/linux/include/000077500000000000000000000000001331522106300156735ustar00rootroot00000000000000fwupdate-12/linux/include/dell-wmi-smi.h000066400000000000000000000044541331522106300203530ustar00rootroot00000000000000/* * dell-wmi-smi - kernel interface to SMI over WMI * * Copyright 2017 Dell, Inc. * * See "COPYING" for license terms. * * Author: Mario Limonciello */ #ifndef _DELL_WMI_SMI_H_ #define _DELL_WMI_SMI_H_ #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) #include #else #include #include /* WMI bus will filter all WMI vendor driver requests through this IOC */ #define WMI_IOC 'W' /* This structure may be modified by the firmware when we enter * system management mode through SMM, hence the volatiles */ struct calling_interface_buffer { uint16_t cmd_class; uint16_t cmd_select; __volatile__ uint32_t input[4]; __volatile__ uint32_t output[4]; } __attribute__((packed)); struct dell_wmi_extensions { uint32_t argattrib; uint32_t blength; uint8_t data[]; } __attribute__((packed)); struct dell_wmi_smbios_buffer { uint64_t length; struct calling_interface_buffer std; struct dell_wmi_extensions ext; } __attribute__((packed)); /* Whitelisted smbios class/select commands */ #define CLASS_TOKEN_READ 0 #define CLASS_TOKEN_WRITE 1 #define SELECT_TOKEN_STD 0 #define SELECT_TOKEN_BAT 1 #define SELECT_TOKEN_AC 2 #define CLASS_FLASH_INTERFACE 7 #define SELECT_FLASH_INTERFACE 3 #define CLASS_ADMIN_PROP 10 #define SELECT_ADMIN_PROP 3 #define CLASS_INFO 17 #define SELECT_RFKILL 11 #define SELECT_APP_REGISTRATION 3 #define SELECT_DOCK 22 /* whitelisted tokens */ #define CAPSULE_EN_TOKEN 0x0461 #define CAPSULE_DIS_TOKEN 0x0462 #define WSMT_EN_TOKEN 0x04EC #define WSMT_DIS_TOKEN 0x04ED /* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */ #define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer) #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0) */ /* these aren't defined upstream but used in fwupdate */ #define DELL_ADMIN_MASK 0xF #define DELL_ADMIN_INSTALLED 0 /* device files to interact with over wmi */ #define DELL_WMI_CHAR "/dev/wmi/dell-smbios" #define TOKENS_SYSFS "/sys/devices/platform/dell-smbios.0/tokens" /* error codes */ #define FWUPDATE_NO_TOKENS_FOUND -1 #define FWUPDATE_LIBSMBIOS_FAILURE -2 #define FWUPDATE_ADMIN_PASSWORD_SET -3 #define FWUPDATE_ESRT_DISABLED 2 #define FWUPDATE_ESRT_ENABLED 3 #endif /* _DELL_WMI_SMI_H_ */ fwupdate-12/linux/include/fwup-version.h.in000066400000000000000000000007321331522106300211170ustar00rootroot00000000000000/* * libfw - library interface to apply firmware updates * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #if !defined(LIBFWUP_H_INSIDE__) #error "Only can be included directly." #endif #ifndef LIBFWUP_VERSION_H #define LIBFWUP_VERSION_H #define LIBFWUP_VERSION (@@FWUP_VERSION@@) #define LIBFWUP_CHECK_VERSION(major,minor) \ LIBFWUP_VERSION >= (major) #endif /* LIBFWUP_VERSION_H */ fwupdate-12/linux/include/fwup.h000066400000000000000000000062631331522106300170340ustar00rootroot00000000000000/* * libfw - library interface to apply firmware updates * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #ifndef LIBFWUP_H #define LIBFWUP_H #define LIBFWUP_H_INSIDE__ #include #include #include #include #include #include #include extern int fwup_supported(void); extern int fwup_esrt_disabled(void); extern int fwup_enable_esrt(void); extern int fwup_version(void); #define FWUP_SUPPORTED_STATUS_UNSUPPORTED 0 #define FWUP_SUPPORTED_STATUS_UNLOCKED 1 #define FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK 2 #define FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK_NEXT_BOOT 3 #define FWUP_RESOURCE_TYPE_UNKNOWN 0 #define FWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE 1 #define FWUP_RESOURCE_TYPE_DEVICE_FIRMWARE 2 #define FWUP_RESOURCE_TYPE_UEFI_DRIVER 3 #define FWUP_RESOURCE_TYPE_FMP 4 #define FWUP_LAST_ATTEMPT_STATUS_SUCCESS 0x00000000 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL 0x00000001 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES 0x00000002 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION 0x00000003 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT 0x00000004 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR 0x00000005 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC 0x00000006 #define FWUP_LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT 0x00000007 #define FWUPDATE_GUID EFI_GUID(0x0abba7dc,0xe516,0x4167,0xbbf5,0x4d,0x9d,0x1c,0x73,0x94,0x16) typedef struct fwup_resource_s fwup_resource; typedef struct fwup_resource_iter_s fwup_resource_iter; extern int fwup_resource_iter_next(fwup_resource_iter *iter, fwup_resource **re); extern int fwup_resource_iter_create(fwup_resource_iter **iter); extern int fwup_resource_iter_destroy(fwup_resource_iter **iter); extern void fwup_resource_free(fwup_resource *re); extern void fwup_use_existing_media_path(int); extern void fwup_set_esp_mountpoint(char *path); const char *fwup_get_esp_mountpoint(void); extern int fwup_set_up_update(fwup_resource *re, uint64_t hw_inst, int infd); extern int fwup_set_up_update_with_buf(fwup_resource *re, uint64_t hw_inst, const void *buf, size_t sz); extern int fwup_set_guid_forced(fwup_resource_iter *iter, fwup_resource **re, const efi_guid_t *guid, bool force); extern int fwup_set_guid(fwup_resource_iter *iter, fwup_resource **re, const efi_guid_t *guid); extern int fwup_clear_status(fwup_resource *re); extern int fwup_get_guid(fwup_resource *re, efi_guid_t **guid); extern int fwup_get_fw_type(fwup_resource *re, uint32_t *type); extern int fwup_get_fw_version(fwup_resource *re, uint32_t *version); extern int fwup_get_lowest_supported_fw_version(fwup_resource *re, uint32_t *version); extern int fwup_get_last_attempt_info(fwup_resource *re, uint32_t *version, uint32_t *status, time_t *when); extern int fwup_get_ux_capsule_info(uint32_t *screen_x_size, uint32_t *screen_y_size); extern const char *fwup_last_attempt_status_to_string (uint64_t status); extern int fwup_print_update_info(void); extern int fwup_get_debug_log(char **utf8, size_t *size); #undef LIBFWUP_H_INSIDE__ #endif /* LIBFWUP_H */ fwupdate-12/linux/libfwup.abixml000066400000000000000000001412521331522106300171230ustar00rootroot00000000000000 fwupdate-12/linux/libfwup.c000066400000000000000000001355151331522106300160760ustar00rootroot00000000000000/* * libfw - library interface to apply firmware updates * * 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 #include #define EFI_TIME efi_time_t #include #include "util.h" #include "ucs2.h" #include "fwup-efi.h" static int verbose; #include "error.h" #include #ifdef FWUPDATE_HAVE_LIBSMBIOS__ #include #include #endif static char *arch_names_32[] = { #if defined(__x86_64__) || defined(__i386__) || defined(__i686__) "ia32", #endif EMPTY }; static int n_arches_32 = sizeof(arch_names_32) / sizeof(arch_names_32[0]); static char *arch_names_64[] = { #if defined(__x86_64__) "x64", #elif defined(__aarch64__) "aa64", #endif EMPTY }; static int n_arches_64 = sizeof(arch_names_64) / sizeof(arch_names_64[0]); #define ESRT_DIR "/sys/firmware/efi/esrt/" #define get_esrt_dir(entries) \ ({ \ char *_esrt_dir = ESRT_DIR; \ char *_alt_dir = getenv("LIBFWUP_ESRT_DIR"); \ char *_ret; \ if (entries) { \ _ret = alloca(strlen(_alt_dir?_alt_dir:_esrt_dir) \ + strlen("entries/") + 1); \ strcpy(_ret, _alt_dir?_alt_dir:_esrt_dir); \ strcat(_ret, "entries/"); \ } else { \ _ret = strdupa(_alt_dir?_alt_dir:_esrt_dir); \ } \ _ret; \ }) static char *esp_mountpoint = FWUP_ESP_MOUNTPOINT; /** * fwup_set_esp_mountpoint: * @path: pointer to a string containing the path to the ESP mountpoint * * The string isn't copied so you should not free it after calling this function */ void fwup_set_esp_mountpoint(char *path) { esp_mountpoint = path; } /** * fwup_get_esp_mountpoint: * * Returns the path to the ESP mountpoint * * @returns: pointer to a string */ const char * fwup_get_esp_mountpoint(void) { return esp_mountpoint; } static int efidp_end_entire(efidp_header *dp) { if (!dp) return 0; if (efidp_type((efidp)dp) != EFIDP_END_TYPE) return 0; if (efidp_subtype((efidp)dp) != EFIDP_END_ENTIRE) return 0; return 1; } static int wmi_supported(void) { if (access(DELL_WMI_CHAR, F_OK) != -1) return 1; return 0; } static int wmi_call_ioctl(struct dell_wmi_smbios_buffer *buffer) { int fd, ret; int error; fd = open(DELL_WMI_CHAR, O_NONBLOCK); if (fd < 0) return fd; ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer); error = errno; close(fd); errno = error; return ret; } static int wmi_read_buffer_size(uint64_t *buffer_size) { FILE *f; size_t read; int error; f = fopen(DELL_WMI_CHAR, "rb"); if (!f) return -1; read = fread(buffer_size, sizeof(uint64_t), 1, f); error = errno; fclose(f); errno = error; if (read != 1) return -1; return 0; } static int wmi_find_token(uint16_t token, uint32_t *location, uint32_t *value) { char value_sysfs[sizeof("ffff_value")]; char location_sysfs[sizeof("ffff_location")]; sprintf(value_sysfs, "%04hhx_value", token); sprintf(location_sysfs, "%04hhx_location", token); *value = get_value_from_file_at_dir(TOKENS_SYSFS, value_sysfs); *location = get_value_from_file_at_dir(TOKENS_SYSFS, location_sysfs); if (*location) return 0; return -1; } static int prepare_buffer_real(struct dell_wmi_smbios_buffer **buffer, uint16_t class, uint16_t select, unsigned int count, ...) { uint64_t buffer_size = 0; int ret; va_list ap; /* the biggest known value provided by any system today */ uint64_t limit = 32768; if (count > 4 || !buffer) { errno = EINVAL; return -errno; } ret = wmi_read_buffer_size(&buffer_size); if (ret < 0 || buffer_size < 1) { errno = ENODEV; return -errno; } if (buffer_size > limit) { errno = EINVAL; return -errno; } *buffer = malloc(buffer_size); if (!*buffer) { errno = ENOMEM; return -errno; } (*buffer)->length = buffer_size; (*buffer)->std.cmd_class = class; (*buffer)->std.cmd_select = select; va_start(ap, count); for (unsigned int i = 0; i < count; i++) { uint32_t arg = va_arg(ap, uint32_t); (*buffer)->std.input[i] = arg; } va_end(ap); return 0; } #define prepare_buffer(buffer, class, select, count, ...) \ ({ \ int ret_; \ ret_ = prepare_buffer_real(buffer, class, select, \ count, ##__VA_ARGS__); \ if (ret_ >= 0) \ *(buffer) = onstack(*(buffer), \ (*(buffer))->length); \ ret_; \ }) static int wmi_token_is_active(uint32_t *location, uint32_t *cmpvalue) { struct dell_wmi_smbios_buffer *ioctl_buffer; int ret; ret = prepare_buffer(&ioctl_buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD, 1, *location); if (ret < 0) return ret; ret = wmi_call_ioctl(ioctl_buffer); if (ret < 0 || ioctl_buffer->std.output[0] != 0) return ret; return (ioctl_buffer->std.output[1] == *cmpvalue); } static int query_token(uint16_t token) { if (wmi_supported()) { uint32_t location = 0; uint32_t cmpvalue = 0; /* locate token */ if (wmi_find_token(token, &location, &cmpvalue) < 0) return -1; /* query actual token status */ return wmi_token_is_active(&location, &cmpvalue); } #ifdef FWUPDATE_HAVE_LIBSMBIOS__ if (!token_is_bool(token)) return -1; if (token_is_active(token) > 0) return 1; #endif return -1; } static int activate_token(uint16_t token) { int ret; if (wmi_supported()) { struct dell_wmi_smbios_buffer *ioctl_buffer; uint32_t location; uint32_t cmpvalue; /* locate token */ if (wmi_find_token(token, &location, &cmpvalue) < 0) return -1; ret = prepare_buffer(&ioctl_buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD, 2, location, 1); if (ret < 0) return ret; ret = wmi_call_ioctl(ioctl_buffer); return ret; } #ifdef FWUPDATE_HAVE_LIBSMBIOS__ token_activate(token); ret = token_is_active(token); if (ret < 0) { efi_error("%d activation failed", token); return FWUPDATE_ADMIN_PASSWORD_SET; } return FWUPDATE_ESRT_DISABLED; #else return FWUPDATE_NO_TOKENS_FOUND; #endif } static int admin_password_present() { int ret; if (wmi_supported()) { struct dell_wmi_smbios_buffer *ioctl_buffer; ret = prepare_buffer(&ioctl_buffer, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP, 0); if (ret < 0) return ret; ret = wmi_call_ioctl(ioctl_buffer); if (ret < 0) return ret; if (ioctl_buffer->std.output[0] != 0 || (ioctl_buffer->std.output[1] & DELL_ADMIN_MASK) == DELL_ADMIN_INSTALLED) return FWUPDATE_ADMIN_PASSWORD_SET; return FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK; } #ifdef FWUPDATE_HAVE_LIBSMBIOS__ uint32_t args[4] = { 0, }, out[4] = { 0, }; if (dell_simple_ci_smi(CLASS_ADMIN_PROP, SELECT_ADMIN_PROP, args, out)) return FWUPDATE_LIBSMBIOS_FAILURE; if (out[0] != 0 || (out[1] & DELL_ADMIN_MASK) == DELL_ADMIN_INSTALLED) return FWUPDATE_ADMIN_PASSWORD_SET; return FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK; #else return FWUPDATE_NO_TOKENS_FOUND; #endif } /* fwup_esrt_disabled tests if ESRT is disabled (but can be enabled) return codes: -1 : the tokens were not found. system is unsupported -2 : libsmbios failure, this scenario shouldn't be reached -3 : admin password is set 2 : ESRT is currently disabled and can be enabled. 3 : tokens were found, will be enabled next boot */ int fwup_esrt_disabled(void) { int ret; ret = query_token(CAPSULE_DIS_TOKEN); if (ret < 0) { ret = query_token(CAPSULE_EN_TOKEN); if (ret > 0) return FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK_NEXT_BOOT; return FWUPDATE_LIBSMBIOS_FAILURE; } return admin_password_present(); } /* fwup_enable_esrt attempts to enable ESRT return codes: <= 0 : failure 1 : already enabled 2 : success 3 : tokens were found, will be enabled next boot */ int fwup_enable_esrt(void) { int rc; rc = fwup_supported(); /* can't enable or already enabled */ if (rc != FWUP_SUPPORTED_STATUS_LOCKED_CAN_UNLOCK) { efi_error("fwup_supported() returned %d", rc); return rc; } /* disabled in BIOS, but supported to be enabled via tool */ rc = query_token(CAPSULE_EN_TOKEN); if (!rc) { efi_error ("DELL_CAPSULE_FIRMWARE_UPDATES_ENABLED is unsupported"); return FWUPDATE_LIBSMBIOS_FAILURE; } activate_token(CAPSULE_EN_TOKEN); return FWUPDATE_ESRT_DISABLED; } /* fwup_supported tests if firmware updating supported return codes: <0 : error 0 : unsupported 1 : supported 2 : ESRT is currently disabled but can be enabled 3 : ESRT is currently disabled but will be enabled on next boot */ int fwup_supported(void) { struct stat buf; int rc; rc = stat(get_esrt_dir(1), &buf); if (rc < 0) { efi_error("ESRT is not present"); /* check if we have the ability to turn on ESRT */ rc = fwup_esrt_disabled(); if (rc < 0) { efi_error("ESRT cannot be enabled"); return FWUP_SUPPORTED_STATUS_UNSUPPORTED; } return rc; } if (buf.st_nlink < 3) { efi_error("ESRT has no entries."); return FWUP_SUPPORTED_STATUS_UNSUPPORTED; } return FWUP_SUPPORTED_STATUS_UNLOCKED; } typedef struct esre_s { efi_guid_t guid; uint32_t fw_type; uint32_t fw_version; uint32_t lowest_supported_fw_version; uint32_t capsule_flags; uint32_t last_attempt_version; uint32_t last_attempt_status; } esre; static void free_info(update_info *info) { if (info) { if (info->dp_ptr) free(info->dp_ptr); free(info); } } static int get_info(efi_guid_t *guid, uint64_t hw_inst, update_info **info) { efi_guid_t varguid = FWUPDATE_GUID; char *varname = NULL; char *guidstr = NULL; int rc; update_info *local; int error; rc = efi_guid_to_str(guid, &guidstr); if (rc < 0) { efi_error("efi_guid_to_str() failed"); return -1; } guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, hw_inst); if (rc < 0) { efi_error("asprintf() failed"); return -1; } varname = onstack(varname, strlen(varname)+1); uint8_t *data = NULL; size_t data_size = 0; uint32_t attributes; rc = efi_get_variable(varguid, varname, &data, &data_size, &attributes); if (rc < 0) { if (errno != ENOENT) { efi_error("efi_get_variable() failed"); return -1; } efi_error_clear(); local = calloc(1, sizeof (*local)); if (!local) { efi_error("calloc(1, %zd) failed", sizeof (*local)); return -1; } local->update_info_version = UPDATE_INFO_VERSION; local->guid = *guid; local->hw_inst = hw_inst; local->dp_ptr = calloc(1, 1024); if (!local->dp_ptr) { efi_error("calloc(1, 1024) failed"); alloc_err: error = errno; free_info(local); errno = error; return -1; } ssize_t sz; sz = efidp_make_end_entire((uint8_t *)local->dp_ptr, 1024); if (sz < 0) { efi_error("efidp_make_end_entire() failed"); goto alloc_err; } *info = local; return 0; } /* If our size is wrong, or our data is otherwise bad, try to delete * the variable and create a new one. */ if (data_size < sizeof (*local) || !data) { if (data) free(data); get_err: rc = efi_del_variable(varguid, varname); if (rc < 0) { efi_error("efi_del_variable() failed"); return -1; } rc = get_info(guid, hw_inst, info); if (rc < 0) efi_error("get_info() failed"); return rc; } local = (update_info *)data; if (local->update_info_version != UPDATE_INFO_VERSION) { efi_error("fwupdate saved state version mismatch"); goto get_err; } ssize_t sz = efidp_size((efidp)local->dp_buf); if (sz < 0) { efi_error("efidp_size() failed"); free(data); errno = EINVAL; return -1; } efidp_header *dp = malloc((size_t)sz); if (!dp) { efi_error("malloc(%zd) failed", (size_t)sz); free(data); errno = ENOMEM; return -1; } memcpy(dp, local->dp_buf, (size_t)sz); local->dp_ptr = dp; *info = local; return 0; } static int put_info(update_info *info) { efi_guid_t varguid = FWUPDATE_GUID; ssize_t dps, is; char *guidstr = NULL; char *varname; int error; int rc; rc = efi_guid_to_str(&info->guid, &guidstr); if (rc < 0) { efi_error("efi_guid_to_str() failed"); err: return rc; } guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, info->hw_inst); if (rc < 0) { efi_error("asprintf() failed"); goto err; } varname = onstack(varname, strlen(varname)+1); dps = efidp_size((efidp)info->dp_ptr); /* make sure dps is at least big enough to have our structure */ if (dps < 0 || (size_t)dps < sizeof(*info)) { efi_error("device path size (%zd) was unreasonable", dps); errno = EINVAL; return -1; } /* Make sure sizeof(*info) + dps won't integer overflow */ if (((size_t)dps >= SSIZE_MAX - sizeof(*info)) || /* Make sure extra hard by just picking an astonishingly large * value that's merely very very unlikely... */ ((ssize_t)dps > sysconf(_SC_PAGESIZE) * 100)) { efi_error("device path size (%zd) would overflow", dps); errno = EOVERFLOW; return -1; } is = sizeof(*info) + dps - sizeof(info->dp_ptr); update_info *info2; info2 = malloc(is); if (!info2) return -1; memcpy(info2, info, sizeof(*info)); memcpy(info2->dp_buf, info->dp_ptr, dps); uint32_t attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; rc = efi_set_variable(varguid, varname, (uint8_t *)info2, is, attributes, 0644); error = errno; if (rc < 0) efi_error("efi_set_variable(%s) failed", varname); free(info2); errno = error; return rc; } static int32_t fwup_screen_xsize; static int32_t fwup_screen_ysize; typedef struct fwup_resource_s { esre esre; bool allocated; /* was this allocated *without* a fwup_resource_iter? */ update_info *info; } fwup_resource; typedef struct fwup_resource_iter_s { DIR *dir; int dirfd; int add_ux_capsule; fwup_resource re; } fwup_resource_iter; int fwup_resource_iter_create(fwup_resource_iter **iter) { int error; char *path; uint32_t x, y; char *env; if (!iter) { efi_error("invalid iter"); errno = EINVAL; return -1; } fwup_resource_iter *new = calloc(1, sizeof (fwup_resource_iter)); if (!new) { efi_error("calloc(1, %zd) failed", sizeof (fwup_resource_iter)); errno = ENOMEM; return -1; } path = get_esrt_dir(1); if (!path) { efi_error("get_esrt_dir(1) failed"); goto err; } new->dir = opendir(path); if (!new->dir) { efi_error("opendir(path) failed"); goto err; } new->dirfd = dirfd(new->dir); if (new->dirfd < 0) { efi_error("dirfd() failed"); goto err; } new->add_ux_capsule = true; env = getenv("LIBFWUP_ADD_UX_CAPSULE"); if (env && !strcmp(env, "0") && fwup_get_ux_capsule_info(&x, &y) >= 0) new->add_ux_capsule = false; *iter = new; return 0; err: error = errno; if (new) { if (new->dir) closedir(new->dir); free(new); } errno = error; return -1; } static void clear_res(fwup_resource *res) { if (res) { bool allocated = res->allocated; if (res->info) { if (res->info->dp_ptr) free(res->info->dp_ptr); free(res->info); } memset(res, 0, sizeof (*res)); res->allocated = allocated; } } void fwup_resource_free(fwup_resource *res) { if (!res) return; if (res->allocated != true) return; memset(res, '\0', sizeof(*res)); free(res); } int fwup_resource_iter_destroy(fwup_resource_iter **iterp) { if (!iterp) { efi_error("invalid iter"); errno = EINVAL; return -1; } fwup_resource_iter *iter = *iterp; if (!iter) return 0; clear_res(&iter->re); if (iter->dir) closedir(iter->dir); free(iter); *iterp = NULL; return 0; } static fwup_resource fwup_ux_capsule = { .esre.fw_type = FWUP_RESOURCE_TYPE_SYSTEM_FIRMWARE, .esre.fw_version = 1, .esre.lowest_supported_fw_version = 1, .esre.capsule_flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET, .esre.last_attempt_version = 1, .esre.last_attempt_status = FWUP_LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL, .info = NULL }; static fwup_resource * make_ux_capsule_entry(void) { int rc; fwup_ux_capsule.esre.guid = efi_guid_ux_capsule; if (fwup_ux_capsule.info == NULL) { rc = get_info(&fwup_ux_capsule.esre.guid, 0, &fwup_ux_capsule.info); if (rc < 0) { efi_error("get_info() failed"); return NULL; } } return &fwup_ux_capsule; } int fwup_resource_iter_next(fwup_resource_iter *iter, fwup_resource **re) { fwup_resource *res; if (!iter || !re) { errno = EINVAL; return -1; } res = &iter->re; clear_res(res); struct dirent *entry; while (1) { errno = 0; entry = readdir(iter->dir); if (!entry) { if (errno != 0) { efi_error("readdir failed"); return -1; } if (iter->add_ux_capsule) { iter->add_ux_capsule = false; *re = make_ux_capsule_entry(); if (*re) return 1; } else if (fwup_ux_capsule.info) { free_info(fwup_ux_capsule.info); fwup_ux_capsule.info = NULL; } return 0; } if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) break; } int dfd = openat(iter->dirfd, entry->d_name, O_RDONLY|O_DIRECTORY); if (dfd < 0) { efi_error("openat() failed"); return -1; } char *class = NULL; get_string_from_file(dfd, "fw_class", &class); int rc = efi_str_to_guid(class, &res->esre.guid); if (rc < 0) { efi_error("efi_str_to_guid() failed"); return rc; } res->esre.fw_type = get_value_from_file(dfd, "fw_type"); res->esre.fw_version = get_value_from_file(dfd, "fw_version"); res->esre.capsule_flags = get_value_from_file(dfd, "capsule_flags"); res->esre.last_attempt_status = get_value_from_file(dfd, "last_attempt_status"); res->esre.last_attempt_version = get_value_from_file(dfd, "last_attempt_version"); res->esre.lowest_supported_fw_version = get_value_from_file(dfd, "lowest_supported_fw_version"); rc = get_info(&res->esre.guid, 0, &res->info); if (rc < 0) { efi_error("get_info() failed"); return rc; } res->info->capsule_flags = res->esre.capsule_flags; *re = res; return 1; } int fwup_set_guid_forced(fwup_resource_iter *iter, fwup_resource **re, const efi_guid_t *guid, bool force) { fwup_resource *res; errno = 0; if (!re) { efi_error("invalid argument 're'"); errno = EINVAL; return -1; } if (!iter && (!*re && !force)) { efi_error("invalid argument '%s'", iter ? "iter" : "re"); errno = EINVAL; return -1; } if (iter) { res = &iter->re; res->esre.guid = *guid; *re = res; } else if (force) { res = calloc(1, sizeof (*res)); if (!res) { efi_error("couldn't allocate resource"); errno = ENOMEM; return -1; } res->esre.guid = *guid; res->allocated = true; *re = res; } else { efi_error("No such guid"); errno = ENOENT; return -1; } return 1; } int fwup_set_guid(fwup_resource_iter *iter, fwup_resource **re, const efi_guid_t *guid) { return fwup_set_guid_forced(iter, re, guid, false); } int fwup_clear_status(fwup_resource *re) { if (!re) { efi_error("invalid resource"); errno = EINVAL; return -1; } int rc; re->info->status = 0; rc = put_info(re->info); if (rc < 0) efi_error("put_info() failed"); return rc; } int fwup_get_guid(fwup_resource *re, efi_guid_t **guid) { if (!re || !guid) { efi_error("invalid %s", guid ? "resource" : "guid"); errno = EINVAL; return -1; } *guid = &re->esre.guid; return 0; } int fwup_get_fw_version(fwup_resource *re, uint32_t *version) { if (!re || !version) { efi_error("invalid %s", version ? "resource" : "version"); errno = EINVAL; return -1; } *version = re->esre.fw_version; return 0; } int fwup_get_fw_type(fwup_resource *re, uint32_t *type) { if (!re || !type) { efi_error("invalid %s", type ? "resource" : "type"); errno = EINVAL; return -1; } *type = re->esre.fw_type; return 0; } int fwup_get_lowest_supported_fw_version(fwup_resource *re, uint32_t *version) { if (!re || !version) { efi_error("invalid %s", version ? "resource" : "version"); errno = EINVAL; return -1; } *version = re->esre.lowest_supported_fw_version; return 0; } int fwup_get_attempt_status(fwup_resource *re, uint32_t *status) { if (!re || !status) { efi_error("invalid %s", status ? "resource" : "status"); errno = EINVAL; return -1; } if (re->info->status & FWUPDATE_ATTEMPTED) *status = 1; return 0; } int fwup_get_last_attempt_info(fwup_resource *re, uint32_t *version, uint32_t *status, time_t *when) { if (!re || !version || !status || !when) { efi_error("invalid argument"); errno = EINVAL; return -1; } if (!re->info->status) { efi_error("invalid status"); errno = ENOENT; return -1; } if (!(re->info->status & FWUPDATE_ATTEMPTED)) return 0; *version = re->esre.last_attempt_version; *status = re->esre.last_attempt_status; struct tm tm = { .tm_year = re->info->time_attempted.year - 1900, .tm_mon = re->info->time_attempted.month - 1, .tm_mday = re->info->time_attempted.day, .tm_hour = re->info->time_attempted.hour, .tm_min = re->info->time_attempted.minute, .tm_sec = re->info->time_attempted.second, .tm_isdst = re->info->time_attempted.daylight, }; *when = mktime(&tm); return 1; } /* XXX PJFIX: this should be in efiboot-loadopt.h in efivar */ #define LOAD_OPTION_ACTIVE 0x00000001 static int get_paths(char **shim_fs_path, char **fwup_fs_path, char **fwup_esp_path) { int ret = -1; int rc; char *shim_fs_path_tmpl = NULL; char *fwup_fs_path_tmpl = NULL; uint8_t fwup_esp_path_tmpl[] = "\\fwup"; char *shim_fs_path_tmp = NULL; char *fwup_fs_path_tmp = NULL; char *fwup_esp_path_tmp = NULL; uint64_t firmware_bits = 0; rc = asprintf(&shim_fs_path_tmpl, "%s/EFI/%s/shim", esp_mountpoint, FWUP_EFI_DIR_NAME); if (rc < 0) { efi_error("asprintf failed"); goto out; } rc = asprintf(&fwup_fs_path_tmpl, "%s/EFI/%s/fwup", esp_mountpoint, FWUP_EFI_DIR_NAME); if (rc < 0) { efi_error("asprintf failed"); goto out; } firmware_bits = get_value_from_file_at_dir("/sys/firmware/efi/", "fw_platform_size"); char **arch_names = firmware_bits == 64 ? arch_names_64 : arch_names_32; int n_arches = firmware_bits == 64 ? n_arches_64 : n_arches_32; int i; *shim_fs_path = NULL; *fwup_fs_path = NULL; *fwup_esp_path = NULL; find_matching_file(shim_fs_path_tmpl, ".efi", arch_names, n_arches, &shim_fs_path_tmp); i = find_matching_file(fwup_fs_path_tmpl, ".efi", arch_names, n_arches, &fwup_fs_path_tmp); if (i < 0) { efi_error("could not find fwup on ESP"); errno = ENOENT; ret = i; goto out; } rc = asprintf(&fwup_esp_path_tmp, "%s%s.efi", fwup_esp_path_tmpl, arch_names[i]); if (rc < 0) { efi_error("asprintf failed"); goto out; } if (shim_fs_path_tmp) { *shim_fs_path = strdup(shim_fs_path_tmp); if (!*shim_fs_path) { efi_error("strdup failed"); goto out; } } if (fwup_fs_path_tmp) { *fwup_fs_path = strdup(fwup_fs_path_tmp); if (!*fwup_fs_path) { efi_error("strdup failed"); goto out; } } if (fwup_esp_path_tmp) *fwup_esp_path = fwup_esp_path_tmp; free(shim_fs_path_tmpl); free(fwup_fs_path_tmpl); return 0; out: if (shim_fs_path_tmpl) free(shim_fs_path_tmpl); if (fwup_fs_path_tmpl) free(fwup_fs_path_tmpl); if (*shim_fs_path) free(*shim_fs_path); if (*fwup_fs_path) free(*fwup_fs_path); if (fwup_esp_path_tmp) free(fwup_esp_path_tmp); return ret; } static int add_to_boot_order(uint16_t boot_entry) { uint16_t *boot_order = NULL, *new_boot_order = NULL; size_t boot_order_size = 0; uint32_t attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; int rc; unsigned int i = 0; rc = efi_get_variable_size(efi_guid_global, "BootOrder", &boot_order_size); if (rc == ENOENT) { boot_order_size = 0; efi_error_clear(); } else if (rc < 0) { efi_error("efi_get_variable_size() failed"); return rc; } if (boot_order_size != 0) { rc = efi_get_variable(efi_guid_global, "BootOrder", (uint8_t **)&boot_order, &boot_order_size, &attr); if (rc < 0) { efi_error("efi_get_variable() failed"); goto out; } for (i = 0; i < boot_order_size / sizeof (uint16_t); i++) { uint16_t val = boot_order[i]; if (val == boot_entry) { rc = 0; goto out; } } } new_boot_order = malloc(boot_order_size + sizeof (uint16_t)); if (!new_boot_order) { efi_error("calloc(1, %zd) failed", boot_order_size + sizeof (uint16_t)); return -1; } if (boot_order_size != 0) memcpy(new_boot_order, boot_order, boot_order_size); i = boot_order_size / sizeof (uint16_t); new_boot_order[i] = boot_entry; boot_order_size += sizeof (uint16_t); rc = efi_set_variable(efi_guid_global, "BootOrder", (uint8_t *)new_boot_order, boot_order_size, attr, 0644); if (rc < 0) efi_error("efi_set_variable() failed"); out: if (boot_order) free(boot_order); if (new_boot_order) free(new_boot_order); return rc; } static int set_up_boot_next(void) { ssize_t sz, dp_size = 0; uint8_t *dp_buf = NULL; int rc; int saved_errno; int ret = -1; uint16_t *loader_str = NULL; size_t loader_sz = 0; char *shim_fs_path = NULL; char *fwup_fs_path = NULL; char *fwup_esp_path = NULL; int use_fwup_path = 0; char *label = NULL; uint8_t *opt=NULL; ssize_t opt_size=0; uint32_t attributes = LOAD_OPTION_ACTIVE; rc = get_paths(&shim_fs_path, &fwup_fs_path, &fwup_esp_path); if (rc < 0 || (!shim_fs_path && (!fwup_fs_path || !fwup_esp_path))) { efi_error("could not find paths for shim and fwup"); goto out; } if (!shim_fs_path) use_fwup_path = 1; sz = efi_generate_file_device_path(dp_buf, dp_size, use_fwup_path ? fwup_fs_path : shim_fs_path, EFIBOOT_OPTIONS_IGNORE_FS_ERROR| EFIBOOT_ABBREV_HD); if (sz < 0) { efi_error("efi_generate_file_device_path() failed"); goto out; } dp_size=sz; dp_buf = calloc(1, dp_size); if (!dp_buf) { efi_error("calloc(1, %zd) failed", dp_size); goto out; } if (!use_fwup_path) { loader_str = utf8_to_ucs2((uint8_t *)fwup_esp_path, -1); if (loader_str == NULL) { efi_error("utf8_to_ucs2() failed"); goto out; } loader_sz = ucs2len(loader_str, -1) * 2; if (loader_sz < 2) { efi_error("ucs2len(fwup_esp_path) returned %zu", loader_sz); goto out; } loader_sz += 2; loader_str = onstack(loader_str, loader_sz); } sz = efi_generate_file_device_path(dp_buf, dp_size, use_fwup_path ? fwup_fs_path : shim_fs_path, EFIBOOT_OPTIONS_IGNORE_FS_ERROR| EFIBOOT_ABBREV_HD); if (sz != dp_size) { efi_error("efi_generate_file_device_path() failed"); goto out; } rc = asprintf(&label, "Linux-Firmware-Updater %s", fwup_esp_path); if (rc < 0) { efi_error("asprintf() failed"); goto out; } sz = efi_loadopt_create(opt, opt_size, attributes, (efidp)dp_buf, dp_size, (uint8_t *)label, (uint8_t *)loader_str, loader_sz); if (sz < 0) { efi_error("efi_loadopt_create() failed"); goto out; } opt = calloc(1, sz); if (!opt) { efi_error("calloc(1, %zd) failed", sz); goto out; } opt_size = sz; sz = efi_loadopt_create(opt, opt_size, attributes, (efidp)dp_buf, dp_size, (uint8_t *)label, (uint8_t *)loader_str, loader_sz); if (sz != opt_size) { efi_error("loadopt size was unreasonable."); goto out; } int set_entries[0x10000 / sizeof(int)] = {0,}; efi_guid_t *guid = NULL; char *name = NULL; uint32_t boot_next = 0x10000; int found = 0; uint8_t *var_data = NULL; size_t var_data_size = 0; uint32_t attr; efi_load_option *loadopt = NULL; while ((rc = efi_get_next_variable_name(&guid, &name)) > 0) { if (efi_guid_cmp(guid, &efi_guid_global)) continue; int scanned=0; uint16_t entry=0; rc = sscanf(name, "Boot%hX%n", &entry, &scanned); if (rc < 0) { efi_error("sscanf failed"); goto out; } if (rc != 1) continue; if (scanned != 8) continue; int div = entry / (sizeof(set_entries[0]) * 8); int mod = entry % (sizeof(set_entries[0]) * 8); set_entries[div] |= 1 << mod; rc = efi_get_variable(*guid, name, &var_data, &var_data_size, &attr); if (rc < 0) { efi_error("efi_get_variable() failed"); continue; } loadopt = (efi_load_option *)var_data; if (!efi_loadopt_is_valid(loadopt, var_data_size)) { efi_error("load option was invalid"); do_next: free(var_data); continue; } sz = efi_loadopt_pathlen(loadopt, var_data_size); if (sz != efidp_size((efidp)dp_buf)) { efi_error("device path doesn't match"); goto do_next; } efidp found_dp = efi_loadopt_path(loadopt, var_data_size); if (memcmp(found_dp, dp_buf, sz)) { efi_error("device path doesn't match"); goto do_next; } if ((ssize_t)var_data_size != opt_size) { efi_error("variable data doesn't match"); goto do_next; } if (memcmp(loadopt, opt, opt_size)) { efi_error("load option doesn't match"); goto do_next; } found = 1; boot_next = entry; efi_error_clear(); break; } if (rc < 0) { efi_error("failed to find boot variable"); goto out; } if (found) { efi_loadopt_attr_set(loadopt, LOAD_OPTION_ACTIVE); rc = efi_set_variable(*guid, name, var_data, var_data_size, attr, 0644); free(var_data); if (rc < 0) { efi_error("could not set boot variable active"); goto out; } } else { char boot_next_name[] = "Boot####"; for (uint32_t value = 0; value < 0x10000; value++) { int div = value / (sizeof(set_entries[0]) * 8); int mod = value % (sizeof(set_entries[0]) * 8); if (set_entries[div] & (1<= 0x10000) { efi_error("no free boot variables!"); goto out; } sprintf(boot_next_name, "Boot%04hX", boot_next & 0xffff); rc = efi_set_variable(efi_guid_global, boot_next_name, opt, opt_size, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0644); if (rc < 0) { efi_error("could not set boot variable"); goto out; } } /* XXX TODO: conditionalize this on the UEFI version. */ rc = add_to_boot_order(boot_next); if (rc < 0) efi_error("could not set BootOrder"); else efi_error_clear(); uint16_t real_boot_next = boot_next; rc = efi_set_variable(efi_guid_global, "BootNext", (uint8_t *)&real_boot_next, 2, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0644); if (rc < 0) efi_error("could not set BootNext"); else efi_error_clear(); ret = rc; out: saved_errno = errno; if (dp_buf) free(dp_buf); if (opt) free(opt); if (label) free(label); if (fwup_esp_path) free(fwup_esp_path); if (fwup_fs_path) free(fwup_fs_path); if (shim_fs_path) free(shim_fs_path); errno = saved_errno; return ret; } /** * get_existing_media_path: * @info: the #update_info * * Return a media path to use for the update which has already been used by * this specific GUID. * * Returns: a media path, or %NULL if no such path exists. */ static char * get_existing_media_path(update_info *info) { int rc; char *relpath = NULL; char *fullpath = NULL; uint16_t *ucs2file = NULL; uint16_t ucs2len = 0; /* never set */ if (!info->dp_ptr) goto out; if (efidp_end_entire(info->dp_ptr)) goto out; /* find UCS2 string */ const_efidp idp = (const_efidp)info->dp_ptr; while (1) { if (efidp_type(idp) == EFIDP_END_TYPE && efidp_subtype(idp) == EFIDP_END_ENTIRE) break; if (efidp_type(idp) != EFIDP_MEDIA_TYPE || efidp_subtype(idp) !=EFIDP_MEDIA_FILE) { rc = efidp_next_node(idp, &idp); if (rc < 0) break; continue; } ucs2file = (uint16_t *)((uint8_t *)idp + 4); ucs2len = efidp_node_size(idp) - 4; break; } /* nothing found */ if (!ucs2file || ucs2len <= 0) goto out; /* convert to something sane */ relpath = ucs2_to_utf8(ucs2file, ucs2len / sizeof (uint16_t)); if (!relpath) goto out; /* convert '\' to '/' */ untilt_slashes(relpath); /* build a complete path */ rc = asprintf(&fullpath, "%s%s", esp_mountpoint, relpath); if (rc < 0) fullpath = NULL; out: free(relpath); return fullpath; } static bool use_existing_media_path = true; /** * fwup_use_existing_media_path: * @use_existing_media_path_: 0 or 1 * * set use_existing_media_path, used in get_fd_and_media_path * to know if we have to reuse the filename register for this * update GUID in the firmware. */ void fwup_use_existing_media_path(int use_existing_media_path_) { use_existing_media_path = use_existing_media_path_; } /** * get_fd_and_media_path: * @info: the #update_info * @path: (out): the path * * Opens a suitable file descriptor and sets a media path to use for the update. * * Returns: a FD, or -1 for error */ static int get_fd_and_media_path(update_info *info, char **path) { char *directory = NULL; char *fullpath = NULL; int fd = -1; int rc; bool make_new_path = false; /* look for an existing variable that we've used before for this * update GUID, and reuse the filename so we don't wind up * littering the filesystem with old updates */ if (use_existing_media_path) fullpath = get_existing_media_path (info); if (fullpath) { fd = open(fullpath, O_CREAT|O_TRUNC|O_CLOEXEC|O_RDWR, 0600); if (fd < 0) { efi_error("open of %s failed", fullpath); if (errno == ENOENT) make_new_path = true; else goto out; } } else { make_new_path = true; } if (make_new_path) { /* fall back to creating a new file from scratch */ rc = asprintf(&directory, "%s/EFI/%s/fw", esp_mountpoint, FWUP_EFI_DIR_NAME); if (rc < 0) { efi_error("asprintf directory failed"); return fd; } rc = mkdir(directory, 0775); if (rc < 0 && errno != EEXIST) { efi_error("failed to make %s", directory); goto out; } rc = asprintf(&fullpath, "%s/fwupdate-XXXXXX.cap", directory); if (rc < 0) { efi_error("asprintf fullpath failed"); goto out; } fd = mkostemps(fullpath, 4, O_CREAT|O_TRUNC|O_CLOEXEC); if (fd < 0) { efi_error("mkostemps(%s) failed", fullpath); goto out; } efi_error_clear(); } /* success, so take ownership of the string */ if (path) { *path = fullpath; fullpath = NULL; } out: free(directory); free(fullpath); return fd; } /** * set_efidp_header: * @info: the #update_info * @path: the path * * Update the device path. * * Returns: a FD, or -1 for error */ static int set_efidp_header(update_info *info, const char *path) { int rc = 0; ssize_t req; ssize_t sz; uint8_t *dp_buf = NULL; /* get the size of the path first */ req = efi_generate_file_device_path(NULL, 0, path, EFIBOOT_OPTIONS_IGNORE_FS_ERROR | EFIBOOT_ABBREV_HD); if (req < 0) { rc = -1; goto out; } if (req <= 4) { /* if we just have an end device path, it's not going to work. */ rc = EINVAL; goto out; } dp_buf = calloc(1, req); if (!dp_buf) { rc = -1; goto out; } /* actually get the path this time */ efidp_header *dp = (efidp_header *)dp_buf; sz = efi_generate_file_device_path(dp_buf, req, path, EFIBOOT_OPTIONS_IGNORE_FS_ERROR | EFIBOOT_ABBREV_HD); if (sz < 0) { rc = -1; goto out; } /* @info owns this now */ if (info->dp_ptr) free(info->dp_ptr); info->dp_ptr = dp; dp_buf = NULL; out: free(dp_buf); return rc; } static int get_bmp_size(uint8_t *buf, size_t buf_size, int *height, int *width) { uint32_t ui32; if (buf_size < 26) { invalid: errno = EINVAL; return -1; } if (memcmp(buf, "BM", 2) != 0) goto invalid; memcpy(&ui32, buf+10, 4); if (ui32 < 26) goto invalid; memcpy(&ui32, buf+14, 4); if (ui32 < 26 - 14) goto invalid; memcpy(width, buf+18, 4); memcpy(height, buf+22, 4); return 0; } #define fbdir "/sys/bus/platform/drivers/efi-framebuffer/efi-framebuffer.0" static int read_efifb_info(int *height, int *width) { *height = get_value_from_file_at_dir(fbdir, "height"); *width = get_value_from_file_at_dir(fbdir, "width"); return 0; } typedef struct { efi_guid_t guid; uint32_t header_size; uint32_t flags; uint32_t capsule_image_size; } efi_capsule_header_t; static int write_ux_capsule_header(FILE *fin, FILE *fout) { int rc = -1; int bgrt_x, bgrt_y; int bgrt_height, bgrt_width; int screen_x, screen_y; int height, width; uint8_t *buf = NULL; size_t buf_size = 0; ux_capsule_header_t header; size_t size; int error; long header_pos; efi_capsule_header_t capsule_header = { .flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET, .guid = efi_guid_ux_capsule, .header_size = sizeof(efi_capsule_header_t), .capsule_image_size = 0 }; bgrt_x = get_value_from_file_at_dir("/sys/firmware/acpi/bgrt", "xoffset"); if (bgrt_x < 0) { rc = bgrt_x; goto out; } bgrt_y = get_value_from_file_at_dir("/sys/firmware/acpi/bgrt", "yoffset"); if (bgrt_y < 0) { rc = bgrt_y; goto out; } rc = read_file_at_dir("/sys/firmware/acpi/bgrt", "image", &buf, &buf_size); if (rc < 0) return rc; rc = get_bmp_size(buf, buf_size, &bgrt_height, &bgrt_width); if (rc < 0) goto out; rc = read_efifb_info(&screen_x, &screen_y); if (rc < 0) goto out; header_pos = ftell(fin); if (header_pos < 0) goto out; buf_size = fread(buf, 1, 26, fin); if (buf_size < 26) goto out; rc = get_bmp_size(buf, buf_size, &height, &width); if (rc < 0) goto out; rc = fseek(fin, 0, SEEK_END); if (rc < 0) goto out; capsule_header.capsule_image_size = ftell(fin) + sizeof(efi_capsule_header_t) + sizeof(header); rc = fseek(fin, header_pos, SEEK_SET); if (rc < 0) goto out; memset(&header, '\0', sizeof(header)); header.version = 1; header.image_type = 0; header.reserved = 0; header.x_offset = (screen_x / 2) - (width / 2); header.y_offset = bgrt_y + bgrt_height; size = fwrite(&capsule_header, capsule_header.header_size, 1, fout); if (size != 1) { rc = -1; goto out; } size = fwrite(&header, sizeof(header), 1, fout); if (size != 1) { rc = -1; goto out; } fflush(fout); size = fcopy_file(fin, fout); if (size == 0) { rc = -1; goto out; } fflush(fout); rc = 0; out: error = errno; if (buf) free(buf); errno = error; return rc; } /** * fwup_set_up_update * @re: A %fwup_resource. * @hw_inst: A hardware instance -- currently unused. * @infd: file descriptor to the .cap binary * * Sets up a UEFI update using a file descriptor. * * Returns: -1 on error, @errno being set * * Since: 0.3 */ int fwup_set_up_update(fwup_resource *re, uint64_t hw_inst __attribute__((__unused__)), int infd) { char *path = NULL; int outfd = -1; int rc; off_t offset; update_info *info = NULL; FILE *fin = NULL, *fout = NULL; int error; /* check parameters */ if (!re) { efi_error("invalid resource"); errno = EINVAL; return -1; } if (infd < 0) { efi_error("invalid file descriptor"); errno = EINVAL; return -1; } offset = lseek(infd, 0, SEEK_CUR); if (offset < 0) { efi_error("lseek failed"); return -1; } /* get device */ rc = get_info(&re->esre.guid, 0, &info); if (rc < 0) { efi_error("get_info failed."); goto out; } /* get destination */ rc = -1; outfd = get_fd_and_media_path(info, &path); if (outfd < 0) { goto out; } fin = fdopen(infd, "r"); if (!fin) goto out; fout = fdopen(outfd, "w"); if (!fout) goto out; if (!efi_guid_cmp(&re->esre.guid, &efi_guid_ux_capsule)) { rc = write_ux_capsule_header(fin, fout); if (rc < 0) goto out; } rc = fcopy_file(fin, fout); if (rc < 0) goto out; /* set efidp header */ rc = set_efidp_header(info, path); if (rc < 0) goto out; /* save this to the hardware */ info->status = FWUPDATE_ATTEMPT_UPDATE; memset(&info->time_attempted, 0, sizeof(info->time_attempted)); info->capsule_flags = re->esre.capsule_flags; rc = put_info(info); if (rc < 0) { efi_error("put_info failed."); goto out; } /* update the firmware before the bootloader runs */ rc = set_up_boot_next(); if (rc < 0) goto out; out: error = errno; if (path) free(path); if (fin) fclose(fin); if (fout) { fflush(fout); fclose(fout); } free_info(info); if (outfd >= 0) { fsync(outfd); close(outfd); } errno = error; return rc; } /** * fwup_set_up_update_with_buf * @re: A %fwup_resource. * @hw_inst: A hardware instance -- currently unused. * @buf: A memory buffer * @sz: Size of @buf * * Sets up a UEFI update using a pre-allocated buffer. * * Returns: -1 on error, @errno being set * * Since: 0.5 */ int fwup_set_up_update_with_buf(fwup_resource *re, uint64_t hw_inst __attribute__((__unused__)), const void *buf, size_t sz) { char *path = NULL; int fd = -1; int rc = -1; update_info *info = NULL; int error; FILE *fin = NULL, *fout = NULL; /* check parameters */ if (!re) { efi_error("invalid resource"); errno = EINVAL; return -1; } if (buf == NULL || sz == 0) { efi_error("invalid %s", buf == NULL ? "buffer" : "size"); errno = EINVAL; goto out; } /* get device */ rc = get_info(&re->esre.guid, 0, &info); if (rc < 0) { efi_error("get_info() failed."); goto out; } /* get destination */ fd = get_fd_and_media_path(info, &path); if (fd < 0) { efi_error("get_fd_and_media_path() failed"); goto out; } rc = -1; fin = fmemopen((void *)buf, sz, "r"); if (!fin) goto out; fout = fdopen(fd, "w"); if (!fout) goto out; if (!efi_guid_cmp(&re->esre.guid, &efi_guid_ux_capsule)) { rc = write_ux_capsule_header(fin, fout); if (rc < 0) goto out; } rc = fcopy_file(fin, fout); if (rc < 0) goto out; /* set efidp header */ rc = set_efidp_header(info, path); if (rc < 0) goto out; /* save this to the hardware */ info->status = FWUPDATE_ATTEMPT_UPDATE; memset(&info->time_attempted, 0, sizeof(info->time_attempted)); info->capsule_flags = re->esre.capsule_flags; rc = put_info(info); if (rc < 0) { efi_error("put_info failed."); goto out; } /* update the firmware before the bootloader runs */ rc = set_up_boot_next(); if (rc < 0) goto out; rc = 0; out: error = errno; free_info(info); if (fout) fclose(fout); if (fin) fclose(fin); if (fd >= 0) close(fd); errno = error; return rc; } /** * fwup_last_attempt_status_to_string: * @status: the status enum, e.g. %FWUP_LAST_ATTEMPT_STATUS_SUCCESS. * * Return a string representation of the last attempt status. * * Returns: A const string * * Since: 0.5 */ const char * fwup_last_attempt_status_to_string (uint64_t status) { if (status == FWUP_LAST_ATTEMPT_STATUS_SUCCESS) return "Success"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL) return "Unsuccessful"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES) return "Insufficient resources"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION) return "Incorrect version"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT) return "Invalid firmware format"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR) return "Authentication signing error"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC) return "AC power required"; if (status == FWUP_LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT) return "Battery level is too low"; return NULL; } /** * fwup_print_update_info: * Print the information of firmware update status. * * Returns: -1 on error, @errno being set * * Since: 0.5 */ int fwup_print_update_info(void) { fwup_resource_iter *iter; int id; int rc; rc = fwup_resource_iter_create(&iter); if (rc < 0) { if (errno != ENOENT) efi_error(_("Could not create iterator")); return -1; } fwup_resource *re = NULL; id = 0; while ((rc = fwup_resource_iter_next(iter, &re)) > 0) { update_info *info = re->info; efi_guid_t *guid = &info->guid; char *id_guid = NULL; ssize_t dp_sz; char *path; rc = efi_guid_to_id_guid(guid, &id_guid); if (rc < 0) break; dp_sz = efidp_format_device_path(NULL, 0, (const_efidp)info->dp_ptr, 4096); if (dp_sz <= 0) { errno = EINVAL; rc = -1; free(id_guid); break; } path = malloc(dp_sz); if (!path) { rc = -1; free(id_guid); break; } if (efidp_format_device_path(path, dp_sz, (const_efidp)info->dp_ptr, 4096) != dp_sz) { errno = EINVAL; rc = -1; free(path); free(id_guid); break; } printf("\nInformation for the update status entry %d:\n", id++); printf(" Information Version: %d\n", info->update_info_version); printf(" Firmware GUID: %s\n", id_guid); printf(" Capsule Flags: 0x%08x\n", info->capsule_flags); printf(" Hardware Instance: %" PRIu64 "\n", info->hw_inst); printf(" Update Status: %s\n", info->status == FWUPDATE_ATTEMPT_UPDATE ? "Preparing" : info->status == FWUPDATE_ATTEMPTED ? "Attempted" : "Unknown"); if (info->status == FWUPDATE_ATTEMPTED) { efi_time_t *time_attempted; struct tm tm; time_attempted = (efi_time_t *)&info->time_attempted; tm.tm_year = time_attempted->year - 1900; tm.tm_mon = time_attempted->month - 1; tm.tm_mday = time_attempted->day; tm.tm_hour = time_attempted->hour; tm.tm_min = time_attempted->minute; tm.tm_sec = time_attempted->second; tm.tm_isdst = time_attempted->daylight; printf(" Attempted Time: "); if (mktime(&tm) != (time_t)-1) printf("%s", asctime(&tm)); else printf("Unknown\n"); } printf(" Capsule File Path: %s\n", path); free(path); free(id_guid); } fwup_resource_iter_destroy(&iter); if (rc < 0) return -1; return 0; } static int check_bgrt_status(void) { int version; int type; int status; status = get_value_from_file_at_dir("/sys/firmware/acpi/bgrt", "status"); if (status != 1) { errno = ENOSYS; return -1; } type = get_value_from_file_at_dir("/sys/firmware/acpi/bgrt", "type"); if (type != 0) { errno = EINVAL; return -1; } version = get_value_from_file_at_dir("/sys/firmware/acpi/bgrt", "version"); if (version != 1) { errno = ENOTTY; return -1; } return 0; } int fwup_get_ux_capsule_info(uint32_t *screen_x_size, uint32_t *screen_y_size) { static bool once = false; int height, width; int rc; if (once == true) { if (fwup_screen_xsize <= 0 || fwup_screen_ysize <= 0) { errno = ENOSYS; return -1; } if (screen_x_size) *screen_x_size = fwup_screen_xsize; if (screen_y_size) *screen_y_size = fwup_screen_ysize; return 0; } rc = check_bgrt_status(); if (rc < 0) return rc; rc = read_efifb_info(&height, &width); if (rc < 0) return rc; fwup_screen_xsize = width; fwup_screen_ysize = height; once = true; if (width <= 0 || height <= 0) { errno = ENOSYS; return -1; } if (screen_x_size) *screen_x_size = fwup_screen_xsize; if (screen_y_size) *screen_y_size = fwup_screen_ysize; return 0; } int fwup_get_debug_log(char **utf8, size_t *size) { int rc; efi_guid_t fwupdate_guid = FWUPDATE_GUID; uint16_t *data; size_t vsize; uint32_t attributes; char *udata; int error; if (!utf8 || !size) { errno = EINVAL; return -EINVAL; } rc = efi_get_variable(fwupdate_guid, "FWUPDATE_DEBUG_LOG", (uint8_t **)&data, &vsize, &attributes); if (rc < 0) return rc; udata = ucs2_to_utf8(data, (vsize >> 1)); error = errno; free(data); errno = error; if (!udata) { rc = -1; return rc; } *utf8 = udata; *size = vsize >> 1; return 0; } /** * fwup_version * * Returns the installed runtime version of libfwupdate * * Returns: integer version * * Since: 12 */ int fwup_version(void) { return LIBFWUP_VERSION; } fwupdate-12/linux/libfwup.map.in000066400000000000000000000014311331522106300170230ustar00rootroot00000000000000libfwup.so.1 { global: fwup_clear_status; fwup_enable_esrt; fwup_esrt_disabled; fwup_get_attempt_status; fwup_get_debug_log; fwup_get_esp_mountpoint; fwup_get_fw_type; fwup_get_fw_version; fwup_get_guid; fwup_get_last_attempt_info; fwup_get_lowest_supported_fw_version; fwup_get_ux_capsule_info; fwup_last_attempt_status_to_string; fwup_print_update_info; fwup_resource_iter_create; fwup_resource_iter_destroy; fwup_resource_iter_next; fwup_set_esp_mountpoint; fwup_set_guid; fwup_set_guid_force; fwup_set_up_update; fwup_set_up_update_with_buf; fwup_supported; fwup_use_existing_media_path; local: *; }; LIBFWUP_1.11 { global: fwup_set_guid_forced; fwup_resource_free; } libfwup.so.1; LIBFWUP_1.12 { global: fwup_version; } LIBFWUP_1.11; fwupdate-12/linux/sys/000077500000000000000000000000001331522106300150665ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/000077500000000000000000000000001331522106300167025ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/000077500000000000000000000000001331522106300174455ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/esrt/000077500000000000000000000000001331522106300204225ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/esrt/entries/000077500000000000000000000000001331522106300220735ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/000077500000000000000000000000001331522106300233145ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/capsule_flags000066400000000000000000000000041331522106300260410ustar00rootroot000000000000000x1 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/fw_class000066400000000000000000000000451331522106300250370ustar00rootroot00000000000000819b858e-c52c-402f-80e1-5b311b6c1959 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/fw_type000066400000000000000000000000021331522106300247040ustar00rootroot000000000000001 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/fw_version000066400000000000000000000000021331522106300254100ustar00rootroot000000000000001 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/last_attempt_status000066400000000000000000000000021331522106300273330ustar00rootroot000000000000000 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/last_attempt_version000066400000000000000000000000021331522106300274750ustar00rootroot000000000000001 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry0/lowest_supported_fw_version000066400000000000000000000000021331522106300311120ustar00rootroot000000000000001 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/000077500000000000000000000000001331522106300233155ustar00rootroot00000000000000fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/capsule_flags000066400000000000000000000000041331522106300260420ustar00rootroot000000000000000x1 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/fw_class000066400000000000000000000000451331522106300250400ustar00rootroot0000000000000095e0eca8-a73d-449f-befc-d648692bae52 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/fw_type000066400000000000000000000000021331522106300247050ustar00rootroot000000000000001 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/fw_version000066400000000000000000000000021331522106300254110ustar00rootroot000000000000004 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/last_attempt_status000066400000000000000000000000021331522106300273340ustar00rootroot000000000000000 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/last_attempt_version000066400000000000000000000000021331522106300274760ustar00rootroot000000000000000 fwupdate-12/linux/sys/firmware/efi/esrt/entries/entry1/lowest_supported_fw_version000066400000000000000000000000021331522106300311130ustar00rootroot000000000000004 fwupdate-12/linux/sys/firmware/efi/esrt/fw_resource_count000066400000000000000000000000021331522106300240700ustar00rootroot000000000000002 fwupdate-12/linux/sys/firmware/efi/esrt/fw_resource_count_max000066400000000000000000000000021331522106300247350ustar00rootroot000000000000002 fwupdate-12/linux/sys/firmware/efi/esrt/fw_resource_version000066400000000000000000000000021331522106300244250ustar00rootroot000000000000001 fwupdate-12/linux/tester.c000066400000000000000000000010221331522106300157150ustar00rootroot00000000000000 #include #include #include #include int main(void) { setenv("LIBFWUP_ESRT_DIR", "sys/firmware/efi/esrt/", 1); fwup_resource_iter *iter; int rc; rc = fwup_resource_iter_create(&iter); if (rc < 0) err(1, "fwup_resource_iter_create"); while (1) { fwup_resource re; rc = fwup_resource_iter_next(iter, &re); if (rc < 0) err(1, "fwup_resource_iter_next"); if (rc == 0) break; printf("version: %d\n", re.fw_version); } fwup_resource_iter_destroy(&iter); return 0; } fwupdate-12/linux/ucs2.h000066400000000000000000000073561331522106300153100ustar00rootroot00000000000000#ifndef _EFIVAR_UCS2_H #define _EFIVAR_UCS2_H #define ev_bits(val, mask, shift) \ (((val) & ((mask) << (shift))) >> (shift)) /* * Translate from ucs2 to utf8. * This is used to read old firmware paths from our stored state variables. */ static inline char * __attribute__((__unused__)) ucs2_to_utf8(const uint16_t *chars, size_t max) { size_t i, j; char *ret = alloca(max * 3 + 1); /* would be s/3/6 if this were UCS-4 */ if (!ret) return NULL; memset(ret, 0, max * 3 +1); /* would be s/3/6 if this were UCS-4 */ for (i=0, j=0; i < max && chars[i]; 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 /* && chars[i] < 0x10000 */ ) { 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); #if 0 /* * and since this is UCS-2, which has no surrogates, we don't actually * need to check for the rest of the unicode codepoints. They don't * exist in this representation. */ } else if (chars[i] > 0xffff && chars[i] < 0x200000) { ret[j++] = 0xf0 | ev_bits(chars[i], 0x7, 18); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 12); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 6); ret[j] = 0x80| ev_bits(chars[i], 0x3f, 0); } else if (chars[i] > 0x1fffff && chars[i] < 0x4000000) { ret[j++] = 0xf8 | ev_bits(chars[i], 0x3, 24); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 18); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 12); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 6); ret[j] = 0x80 | ev_bits(chars[i], 0x3f, 0); } else if (chars[i] > 0x3ffffff) { ret[j++] = 0xfc | ev_bits(chars[i], 0x1, 30); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 24); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 18); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 12); ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 6); ret[j] = 0x80 | ev_bits(chars[i], 0x3f, 0); #endif } } ret[j] = '\0'; return strdup(ret); } /* * Find the length of a utf8 string, not counting nulls. * This is only ever used on string literal utf8 text from libfwup source. */ static inline size_t __attribute__((__unused__)) utf8len(uint8_t *s, ssize_t limit) { ssize_t i, j; for (i = 0, j = 0; i < (limit >= 0 ? limit : i+1) && s[i] != '\0'; j++, i++) { if (!(s[i] & 0x80)) { ; } else if ((s[i] & 0xc0) == 0xc0 && !(s[i] & 0x20)) { i += 1; } else if ((s[i] & 0xe0) == 0xe0 && !(s[i] & 0x10)) { i += 2; } } return j; } /* * Convert a utf8 string to a ucs2 string. * This is only ever used on string literal utf8 text from libfwup source. */ static inline uint16_t * __attribute__((__unused__)) utf8_to_ucs2(uint8_t *utf8, ssize_t max) { ssize_t i, j; uint16_t *ret = calloc(utf8len(utf8, max) + 1, sizeof (uint16_t)); if (!ret) return NULL; for (i=0, j=0; i < (max >= 0 ? max : i+1) && utf8[i] != '\0'; j++) { uint32_t val = 0; if ((utf8[i] & 0xe0) == 0xe0 && !(utf8[i] & 0x10)) { val = ((utf8[i+0] & 0x0f) << 10) |((utf8[i+1] & 0x3f) << 6) |((utf8[i+2] & 0x3f) << 0); i += 3; } else if ((utf8[i] & 0xc0) == 0xc0 && !(utf8[i] & 0x20)) { val = ((utf8[i+0] & 0x1f) << 6) |((utf8[i+1] & 0x3f) << 0); i += 2; } else { val = utf8[i] & 0x7f; i += 1; } ret[j] = val; } ret[j] = L'\0'; return ret; }; /* * Find the length of a ucs2 string * This is basically only ever called on the output from utf8_to_ucs2() */ static inline size_t __attribute__((__unused__)) ucs2len(uint16_t *s, ssize_t limit) { ssize_t i; for (i = 0; i < (limit >= 0 ? limit : i + 1) && s[i] != L'\0'; i++) ; return i; } #endif /* _EFIVAR_UCS2_H */ fwupdate-12/linux/util.h000066400000000000000000000145241331522106300154040ustar00rootroot00000000000000/* * util.h - common include crud * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #ifndef LIBFW_UTIL_H #define LIBFW_UTIL_H #include #include #include #include #include #include #define _(String) gettext (String) #define Q_(String) dgettext (NULL, String) #define N_(String) (String) #define C_(Context,String) dgettext (Context,String) #define NC_(Context, String) (String) #define EMPTY "" extern int quiet; #define qprintf(fmt, args...) ({ \ if (!quiet) { \ printf((fmt), ## args); \ } \ }) static inline int __attribute__((unused)) read_file_at(int dfd, const char *name, uint8_t **buf, size_t *bufsize) { int saved_errno; uint8_t *p; size_t size = 4096; size_t filesize = 0; ssize_t s = 0; int fd = openat(dfd, name, O_RDONLY); if (fd < 0) return -1; *buf = NULL; uint8_t *newbuf; if (!(newbuf = calloc(size, sizeof (uint8_t)))) goto err; *buf = newbuf; do { p = *buf + filesize; /* size - filesize shouldn't exceed SSIZE_MAX because we're * only allocating 4096 bytes at a time and we're checking that * before doing so. */ s = read(fd, p, size - filesize); if (s < 0 && errno == EAGAIN) { continue; } else if (s < 0) { goto err; } filesize += s; /* only exit for empty reads */ if (s == 0) break; if (filesize >= size) { /* See if we're going to overrun and return an error * instead. */ if (size > (size_t)-1 - 4096) { errno = ENOMEM; goto err; } newbuf = realloc(*buf, size + 4096); if (newbuf == NULL) goto err; *buf = newbuf; memset(*buf + size, '\0', 4096); size += 4096; } } while (1); newbuf = realloc(*buf, filesize + 1); if (newbuf == NULL) goto err; newbuf[filesize] = '\0'; *buf = newbuf; *bufsize = filesize + 1; close(fd); return 0; err: saved_errno = errno; if (fd >= 0) close(fd); if (*buf) { free(*buf); *buf = NULL; *bufsize = 0; } errno = saved_errno; return -1; } static size_t __attribute__((unused)) fcopy_file(FILE *fin, FILE *fout) { int ret = 0; /* copy the input file to the new home */ while (1) { int c; int rc; c = fgetc(fin); if (c == EOF) { if (feof(fin)) { break; } else if (ferror(fin)) { efi_error("read failed"); ret = 0; goto out; } else { efi_error("fgetc() == EOF but no error is set."); errno = EINVAL; ret = 0; goto out; } } rc = fputc(c, fout); if (rc == EOF) { if (feof(fout)) { break; } else if (ferror(fout)) { efi_error("write failed"); ret = 0; goto out; } else { efi_error("fputc() == EOF but no error is set."); errno = EINVAL; ret = 0; goto out; } } else { ret += 1; } } out: return ret; } static int __attribute__((unused)) read_file_at_dir(const char *dirname, const char *filename, uint8_t **buf, size_t *buf_size) { DIR *dir; int dfd; int rc; int error; dir = opendir(dirname); if (!dir) return -1; dfd = dirfd(dir); if (dfd < 0) { error = errno; closedir(dir); errno = error; return -1; } rc = read_file_at(dfd, filename, buf, buf_size); error = errno; close(dfd); closedir(dir); errno = error; return rc; } #define onstack(buf, len) ({ \ char *__newbuf = alloca(len); \ memcpy(__newbuf, buf, len); \ free(buf); \ (void *)__newbuf; \ }) #define get_value_from_file(dfd, file) \ ({ \ uint64_t _val; \ int _rc; \ \ _rc = get_uint64_from_file(dfd, file, &_val); \ if (_rc < 0) \ return -1; \ _val; \ }) #define get_value_from_file_at_dir(dirname, file) \ ({ \ DIR *_dir; \ int _dfd; \ uint64_t _val; \ _dir = opendir(dirname); \ if (!_dir) \ return -1; \ _dfd = dirfd(_dir); \ if (_dfd < 0) { \ closedir(_dir); \ return -1; \ } \ _val = get_value_from_file(_dfd, (file)); \ closedir(_dir); \ _val; \ }) #define find_matching_file(tmpl, suffix, items, n_items, outpath) \ ({ \ char *__path; \ int __rc; \ int __i; \ int __found = 0; \ for (__i = 0; __i < (n_items); __i++) { \ struct stat __statbuf; \ __rc = asprintf(&__path, "%s%s%s", tmpl, \ (items)[__i], suffix); \ if (__rc < 0) \ return -1; \ __rc = stat(__path, &__statbuf); \ if (__rc >= 0) { \ __found = 1; \ break; \ } \ free(__path); \ if (__rc < 0 && errno != ENOENT) \ return __rc; \ } \ if (__found) { \ *(outpath) = onstack(__path, strlen(__path)+1); \ } else { \ __i = -1; \ } \ __i; \ }) #define get_string_from_file(dfd, file, str) \ ({ \ uint8_t *_buf = NULL; \ size_t _bufsize = 0; \ int _rc; \ \ _rc = read_file_at(dfd, file, &_buf, &_bufsize); \ if (_rc < 0) \ return -1; \ \ *str = strndupa((__typeof__(*str))_buf, _bufsize-1); \ (*str)[_bufsize-1] = '\0'; \ free(_buf); \ *str; \ }) static int __attribute__((__unused__)) get_uint64_from_file(int dfd, char *file, uint64_t *value) { uint64_t val = 0; uint8_t *buf = NULL; size_t bufsize = 0; int rc; int error; rc = read_file_at(dfd, file, &buf, &bufsize); if (rc < 0) { error = errno; close(dfd); errno = error; return -1; } val = strtoull((char *)buf, NULL, 0); if (val == ULLONG_MAX) { error = errno; close(dfd); free(buf); errno = error; return -1; } free(buf); *value = val; return 0; } static char * __attribute__((__unused__)) tilt_slashes(char *s) { char *p; for (p = s; *p; p++) if (*p == '/') *p = '\\'; return s; } static char * __attribute__((__unused__)) untilt_slashes(char *s) { char *p; for (p = s; *p; p++) if (*p == '\\') *p = '/'; return s; } typedef struct { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; uint8_t pad1; uint32_t nanosecond; uint16_t timezone; uint8_t daylight; uint8_t pad2; } efi_time_t; #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 #define align(val, align) (((val) + (align) -1 ) & (- (align))) #endif /* LIBFW_UTIL_H */