pax_global_header00006660000000000000000000000064126231165760014523gustar00rootroot0000000000000052 comment=03178919675d277178dc50c7c8941a9fd91e98ef fwupdate-0.5/000077500000000000000000000000001262311657600132065ustar00rootroot00000000000000fwupdate-0.5/.gitignore000066400000000000000000000001301262311657600151700ustar00rootroot00000000000000*.efi *.efi.unsigned *.efi.debug *.efi.build-id *.so *.o *.a *.pc *.spec *.tar.* .*.sw? fwupdate-0.5/COPYING000066400000000000000000000430051262311657600142430ustar00rootroot00000000000000 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-0.5/Make.defaults000066400000000000000000000020071262311657600156130ustar00rootroot00000000000000INSTALL ?= install MAKE ?= make CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy READELF = eu-readelf XGETTEXT = xgettext prefix ?= /usr/ prefix := $(abspath $(prefix))/ ARCH = $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,) ifeq ($(ARCH),x86_64) LIBDIR ?= $(prefix)lib64 endif ifeq ($(ARCH),ia32) LIBDIR ?= $(prefix)lib endif ifeq ($(ARCH),aarch64) LIBDIR ?= $(prefix)lib64 endif ifeq ($(ARCH),arm) LIBDIR ?= $(prefix)lib endif LIBDIR ?= unknown ifeq ($(LIBDIR),unknown) $(error Architecture $(ARCH) is not a supported build target.) endif GNUEFIDIR ?= $(LIBDIR)/gnuefi/ libdir ?= $(LIBDIR) pcdir ?= $(libdir)/pkgconfig/ mandir ?= $(prefix)share/man/ includedir ?= $(prefix)include/ bindir ?= $(prefix)bin/ datadir ?= $(prefix)share/ localedir ?= $(datadir)/locale/ libexecdir ?= $(prefix)libexec/ libdatadir ?= $(prefix)lib/ EFIDIR ?= redhat DEBUGINFO ?= $(prefix)lib/debug/ DEBUGSOURCE ?= $(prefix)src/debug/ TARGETDIR ?= /boot/efi/EFI/$(EFIDIR)/ fwupdate-0.5/Make.version000066400000000000000000000001201262311657600154630ustar00rootroot00000000000000MAJOR_VERSION = 0 MINOR_VERSION = 5 VERSION = $(MAJOR_VERSION).$(MINOR_VERSION) fwupdate-0.5/Makefile000066400000000000000000000031031262311657600146430ustar00rootroot00000000000000TOPDIR=$(shell pwd) include $(TOPDIR)/Make.version include $(TOPDIR)/Make.defaults SUBDIRS = efi linux docs include all clean install : @set -e ; for x in $(SUBDIRS) ; do \ $(MAKE) DESTDIR=$(DESTDIR) TOPDIR=$(TOPDIR) VERSION=$(VERSION) \ LIBDIR=$(LIBDIR) bindir=$(bindir) mandir=$(mandir) \ -C $$x $@ ; \ done fwupdate.spec : fwupdate.spec.in Makefile @sed -e "s,@@VERSION@@,$(VERSION),g" $< > $@ GITTAG = $(VERSION) test-archive: fwupdate.spec @rm -rf /tmp/fwupdate-$(VERSION) /tmp/fwupdate-$(VERSION)-tmp @mkdir -p /tmp/fwupdate-$(VERSION)-tmp @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/fwupdate-$(VERSION)-tmp/ ; tar x ) @git diff | ( cd /tmp/fwupdate-$(VERSION)-tmp/ ; patch -s -p1 -b -z .gitdiff ) @mv /tmp/fwupdate-$(VERSION)-tmp/ /tmp/fwupdate-$(VERSION)/ @cp fwupdate.spec /tmp/fwupdate-$(VERSION)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/fwupdate-$(VERSION).tar.bz2 fwupdate-$(VERSION) @rm -rf /tmp/fwupdate-$(VERSION) @echo "The archive is in fwupdate-$(VERSION).tar.bz2" tag: git tag -s $(GITTAG) refs/heads/master archive: tag fwupdate.spec @rm -rf /tmp/fwupdate-$(VERSION) /tmp/fwupdate-$(VERSION)-tmp @mkdir -p /tmp/fwupdate-$(VERSION)-tmp @git archive --format=tar $(GITTAG) | ( cd /tmp/fwupdate-$(VERSION)-tmp/ ; tar x ) @mv /tmp/fwupdate-$(VERSION)-tmp/ /tmp/fwupdate-$(VERSION)/ @cp fwupdate.spec /tmp/fwupdate-$(VERSION)/ @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/fwupdate-$(VERSION).tar.bz2 fwupdate-$(VERSION) @rm -rf /tmp/fwupdate-$(VERSION) @echo "The archive is in fwupdate-$(VERSION).tar.bz2" fwupdate-0.5/TODO000066400000000000000000000002051262311657600136730ustar00rootroot00000000000000Things 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-0.5/docs/000077500000000000000000000000001262311657600141365ustar00rootroot00000000000000fwupdate-0.5/docs/Makefile000066400000000000000000000014361262311657600156020ustar00rootroot00000000000000SRCDIR = . TOPDIR = $(SRCDIR)/.. include $(TOPDIR)/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_set_up_update.3 \ fwup_clear_status.3 \ fwup_get_guid.3 \ fwup_get_fw_version.3 \ fwup_get_lowest_supported_version.3 \ fwup_get_last_attempt_info.3 all : clean : test : install : $(INSTALL) -d -m 755 $(DESTDIR)/$(mandir)/man1 $(foreach x, $(MAN1TARGETS), $(INSTALL) -m 644 $(x) $(DESTDIR)/$(mandir)/man1/;) $(INSTALL) -d -m 755 $(DESTDIR)/$(mandir)/man3 $(foreach x, $(MAN3TARGETS), $(INSTALL) -m 644 $(x) $(DESTDIR)/$(mandir)/man3/;) .PHONY: all clean test install fwupdate-0.5/docs/fwup_clear_status.3000066400000000000000000000000231262311657600177470ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_get_fw_type.3000066400000000000000000000000231262311657600175720ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_get_fw_version.3000066400000000000000000000000231262311657600202760ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_get_guid.3000066400000000000000000000000231262311657600170450ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_get_last_attempt_info.3000066400000000000000000000000231262311657600216310ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_get_lowest_supported_version.3000066400000000000000000000000231262311657600233040ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_resource_iter_create.3000066400000000000000000000000231262311657600214530ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_resource_iter_destroy.3000066400000000000000000000000231262311657600217010ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_resource_iter_next.3000066400000000000000000000000231262311657600211660ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_set_up_update.3000066400000000000000000000000231262311657600201170ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwup_supported.3000066400000000000000000000000231262311657600173030ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/docs/fwupdate.1000066400000000000000000000010201262311657600160300ustar00rootroot00000000000000.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 .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\-q\fR, \fB\-\-quiet\fR Work quietly .SS "Help options:" .TP \-?, \fB\-\-help\fR Show this help message .TP \fB\-\-usage\fR Display brief usage message fwupdate-0.5/docs/libfwup.3000066400000000000000000000074161262311657600157020ustar00rootroot00000000000000.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_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 .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. .SH AUTHORS .nf Peter Jones .SH SEE ALSO \fBerrno\fR(3), \fBstrerror\fR(3), \fBstrerror_r\fR(3) fwupdate-0.5/docs/libfwup.h.3000066400000000000000000000000231262311657600161130ustar00rootroot00000000000000.so man3/libfwup.3 fwupdate-0.5/efi/000077500000000000000000000000001262311657600137515ustar00rootroot00000000000000fwupdate-0.5/efi/Makefile000066400000000000000000000072211262311657600154130ustar00rootroot00000000000000TOPDIR ?= $(shell pwd)/.. include $(TOPDIR)/Make.version include $(TOPDIR)/Make.defaults CFLAGS ?= -O0 -g3 BUILDFLAGS := $(CFLAGS) -fpic -Wall -fshort-wchar \ -fno-strict-aliasing -fno-merge-constants -ffreestanding \ -fno-stack-protector -fno-stack-check --std=c11 -DCONFIG_$(ARCH) \ -I/usr/include/efi/ -I/usr/include/efi/$(ARCH)/ \ -iquote$(TOPDIR)/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 OBJCOPY_GTE224 = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.* //g' | cut -f1-2 -d.` \>= 2.24) FWUP = fwupdate ifeq ($(ARCH),x86_64) FORMAT = --target efi-app-$(ARCH) BUILDFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc \ -maccumulate-outgoing-args -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 \ -maccumulate-outgoing-args -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) .SECONDARY: $(foreach target,$(TARGETS),$(target).debug $(target).build-id) $(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 \ $(shell $(CC) -print-libgcc-file-name) \ -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 = $(dirname $(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/$(dir $(shell cat $(1).build-id)) ln -sf ../../../../..$(TARGETDIR)$(1) $(DESTDIR)$(DEBUGINFO).build-id/$(shell cat $(1).build-id) ln -sf ../..$(TARGETDIR)$(1).debug $(DESTDIR)$(DEBUGINFO).build-id/$(shell cat $(1).build-id).debug endef 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)/efi $(foreach src,$(wildcard *.[chS]),$(INSTALL) -m 644 $(src) $(DESTDIR)$(DEBUGSOURCE)fwupdate-$(VERSION)/efi/$(src) ;) fwupdate-0.5/efi/dumpesrt.c000066400000000000000000000070011262311657600157560ustar00rootroot00000000000000#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; static int isprint(char c) { if (c < 0x20) return 0; if (c > 0x7e) return 0; return 1; } static UINTN format_hex(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; CHAR16 hexchars[] = L"0123456789abcdef"; int offset = 0; int i; 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 format_text(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; int offset = 0; int i; 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 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; } } static void dump_esrt(VOID *data) { esrt_t *esrt = data; esre1_t *esre1 = (esre1_t *)((UINT8 *)data + sizeof (*esrt)); hexdump(data, sizeof(*esrt)); for (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 (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-0.5/efi/dumpf.c000066400000000000000000000043261262311657600152350ustar00rootroot00000000000000#include #include static int isprint(char c) { if (c < 0x20) return 0; if (c > 0x7e) return 0; return 1; } static UINTN format_hex(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; CHAR16 hexchars[] = L"0123456789abcdef"; int offset = 0; int i; 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 format_text(UINT8 *data, UINTN size, CHAR16 *buf) { UINTN sz = (UINTN)data % 16; int offset = 0; int i; 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 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; } } 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-0.5/efi/elf_aarch64_efi.lds000066400000000000000000000021541262311657600173600ustar00rootroot00000000000000OUTPUT_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-0.5/efi/elf_arm_efi.lds000066400000000000000000000021521262311657600167050ustar00rootroot00000000000000OUTPUT_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-0.5/efi/elf_ia32_efi.lds000066400000000000000000000025351262311657600166710ustar00rootroot00000000000000OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { . = 0; ImageBase = .; .hash : { *(.hash) } /* this MUST come first! */ . = 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-0.5/efi/elf_x86_64_efi.lds000066400000000000000000000023611262311657600170660ustar00rootroot00000000000000/* 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! */ . = 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-0.5/efi/fakeesrt.c000066400000000000000000000030061262311657600157200ustar00rootroot00000000000000#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, 3 }, .esre0 = { guid0, 0, 0, 0, 0x80f1, 0, 0 }, .esre1 = { guid1, 1, 9, 7, 0x80f1, 0, 0 }, }; EFI_STATUS status; EFI_PHYSICAL_ADDRESS mem = 0; InitializeLib(image_handle, systab); status = systab->BootServices->AllocatePages(AllocateAnyPages, EfiRuntimeServicesData, 1, &mem); if (EFI_ERROR(status)) { Print(L"AllocatePages failed: %r\n", status); return status; } VOID *ptr = (VOID *)mem; CopyMem(ptr, &esrt, sizeof (esrt)); status = systab->BootServices->InstallConfigurationTable(&EsrtGuid, ptr); if (EFI_ERROR(status)) { Print(L"InstallConfigurationTable failed: %r\n", status); return status; } return 0; } fwupdate-0.5/efi/fakeesrt2.c000066400000000000000000000030411262311657600160010ustar00rootroot00000000000000#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-0.5/efi/fwupdate.c000066400000000000000000000474161262311657600157500ustar00rootroot00000000000000/* * fwupdate.c - apply firmware updates * * Copyright 2014 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #include #include #include "hexdump.h" #define efidp_header EFI_DEVICE_PATH #define efi_guid_t EFI_GUID 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}}; #include "fwup-efi.h" typedef struct update_table_s { CHAR16 *name; UINT32 attributes; UINTN size; update_info *info; } update_table; static int debugging; /* * 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; rc = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData, pages, &pageaddr); *addr = (void *)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)addr, pages); return rc; } EFI_STATUS read_file(EFI_FILE_HANDLE fh, UINT8 **buf_out, UINTN *buf_size_out) { UINT8 *b = NULL; UINTN bs = 512; UINTN n_blocks = 4096; UINTN i = 0; EFI_STATUS rc; while (1) { void *newb = NULL; rc = allocate(&newb, bs * n_blocks * 2); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, 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"%a:%a():%d: Could not read file: %r\n", __FILE__, __func__, __LINE__, 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, 4, name, &guid, attributes, 0, NULL); } 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"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, size); Print(L"Could not allocate memory.\n"); return EFI_OUT_OF_RESOURCES; } } else if (rc != EFI_NOT_FOUND) { Print(L"%a:%a():%d: " L"Could not get variable \"%s\": %r\n", __FILE__, __func__, __LINE__, name, rc); return rc; } } else { Print(L"%a:%a():%d: " L"GetVariable(%s) succeeded with size=0.\n", __FILE__, __func__, __LINE__, name); return EFI_INVALID_PARAMETER; } rc = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes, &size, buf); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Could not get variable \"%s\": %r\n", __FILE__, __func__, __LINE__, name, rc); FreePool(buf); return rc; } *buf_out = buf; *buf_size_out = size; *attributes_out = attributes; return EFI_SUCCESS; } 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; rc = read_variable(name, fwupdate_guid, (void **)&info, &info_size, &attributes); if (EFI_ERROR(rc)) return rc; 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; } UINTN is = info_size - EFI_FIELD_OFFSET(update_info, dp); EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp; INTN sz = sizeof (EFI_DEVICE_PATH) - is; if (is >= sizeof (EFI_DEVICE_PATH)) sz = DevicePathSize(hdr); if (is != sz || sz < 4) { 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; } 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; updates = AllocateZeroPool(sizeof (update_table *) * n_updates_allocated); if (!updates) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, sizeof (update_table *) * n_updates_allocated); 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"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, 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) { /* 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; new_name = AllocatePool(new_allocation * 2); if (!new_name) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, 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"%a:%a():%d: " L"Could not get variable name: %r\n", __FILE__, __func__, __LINE__, rc); ret = rc; goto err; } /* * If it's not one of our state variables, keep going. */ if (CompareMem(&vendor_guid, &fwupdate_guid, sizeof (vendor_guid))) { 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; new_ups = AllocateZeroPool(sizeof (update_table *) * n_updates_allocated * 2); if (!new_ups) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, sizeof (update_table *) * n_updates_allocated * 2); Print(L"Could not allocate memory.\n"); ret = EFI_OUT_OF_RESOURCES; goto err; } CopyMem(new_ups, updates, sizeof (update_table *) * n_updates_allocated); n_updates_allocated *= 2; FreePool(updates); updates = new_ups; } updates[n_updates]->name = StrDuplicate(vn); rc = get_info(vn, updates[n_updates]); if (EFI_ERROR(rc)) { Print(L"Could not get update info for \"%s\", " L"aborting.\n", vn); ret = rc; goto err; } if (updates[n_updates]->info->status & FWUPDATE_ATTEMPT_UPDATE) { EFI_TIME_CAPABILITIES timecaps = { 0, }; uefi_call_wrapper(RT->GetTime, 2, &updates[n_updates]->info->time_attempted, &timecaps); updates[n_updates]->info->status = FWUPDATE_ATTEMPTED; n_updates++; } else { FreePool(updates[n_updates]->info); FreePool(updates[n_updates]); updates[n_updates] = NULL; } } *n_updates_out = n_updates; *updates_out = updates; return EFI_SUCCESS; err: if (variable_name) FreePool(variable_name); for (int i = 0; i < n_updates && updates[i]; i++) { if (updates[i]->name) FreePool(updates[i]->name); 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"%a:%a():%d: Could not locate device handle: %r\n", __FILE__, __func__, __LINE__, rc); return rc; } } if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH || DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) { Print(L"%a:%a():%d: Could not find appropriate device.\n", __FILE__, __func__, __LINE__); return EFI_UNSUPPORTED; } UINTN sz = *(UINT16 *)file_dp->Length - 4; if (sz <= 6 || sz % 2 != 0) { Print(L"%a:%a():%d: Invalid file device path.\n", __FILE__, __func__, __LINE__); return EFI_INVALID_PARAMETER; } sz /= sizeof (CHAR16); 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"%a:%a():%d: Could not open device interface: %r.\n", __FILE__, __func__, __LINE__, rc); return rc; } rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Could not open volume: %r.\n", __FILE__, __func__, __LINE__, rc); return rc; } rc = uefi_call_wrapper(root->Open, 5, root, fh, filename, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Could not open file \"%s\": %r.\n", __FILE__, __func__, __LINE__, filename, rc); return rc; } return EFI_SUCCESS; } static EFI_STATUS add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out) { EFI_STATUS rc; EFI_FILE_HANDLE fh = NULL; UINT8 *fbuf = NULL; UINTN fsize = 0; EFI_CAPSULE_HEADER *capsule; rc = open_file((EFI_DEVICE_PATH *)update->info->dp, &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); /* * 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 (CompareMem(&update->info->guid, fbuf, sizeof (update->info->guid)) == 0 && /* * 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) { if (debugging) { Print(L"Image has capsule image embedded\n"); Print(L"updates guid: %g\n", &update->info->guid); Print(L"File guid: %g\n", fbuf); } cbd_out->Length = fsize; cbd_out->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; *capsule_out = (EFI_CAPSULE_HEADER *)fbuf; (*capsule_out)->Flags |= update->info->capsule_flags; } else { if (debugging) { Print(L"Image does not have embedded header\n"); Print(L"Allocating %d for capsule header.\n", sizeof (*capsule)+fsize); } rc = allocate((void **)&capsule, sizeof (*capsule) + fsize); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, 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); capsule->Flags = update->info->capsule_flags; capsule->CapsuleImageSize = fsize + sizeof (*capsule); UINT8 *buffer = (UINT8 *)capsule + capsule->HeaderSize; CopyMem(buffer, fbuf, fsize); cbd_out->Length = capsule->CapsuleImageSize; cbd_out->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)capsule; *capsule_out = capsule; free(fbuf, fsize); } 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 = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules, num_updates, &max_capsule_size, &reset); if (debugging) { Print(L"QueryCapsuleCapabilities: %r max: %ld reset:%d\n", rc, max_capsule_size, reset); Print(L"Capsules: %d\n", num_updates); } uefi_call_wrapper(BS->Stall, 1, 1000000); rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates, (EFI_PHYSICAL_ADDRESS)(VOID *)cbd); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Could not apply capsule update: %r\n", __FILE__, __func__, __LINE__, 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"%a:%a():%d: Could not update variable " L"status for \"%s\": %r\n", __FILE__, __func__, __LINE__, 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; volatile register int x = 0; extern char _text, _data; /* * 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", &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; 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 */ 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; rc = allocate((void **)&cbd_data, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); if (EFI_ERROR(rc)) { Print(L"%a:%a():%d: Tried to allocate %d\n", __FILE__, __func__, __LINE__, sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1)); Print(L"fwupdate: Could not allocate memory: %r.\n",rc); return rc; } for (i = 0; i < n_updates; i++) { rc = add_capsule(updates[i], &capsules[i], &cbd_data[i]); if (EFI_ERROR(rc)) { Print(L"fwupdate: Could not build update list: %r\n", rc); return rc; } } 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); 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. */ if (debugging) { Print(L"Reset System\n"); uefi_call_wrapper(BS->Stall, 1, 10000000); } uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, NULL); return EFI_SUCCESS; } fwupdate-0.5/efi/hexdump.h000066400000000000000000000037471262311657600156070ustar00rootroot00000000000000#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; int i; 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; int i; 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-0.5/efi/mkvar.c000066400000000000000000000021271262311657600152370ustar00rootroot00000000000000#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-0.5/efi/mkvar2.c000066400000000000000000000035501262311657600153220ustar00rootroot00000000000000#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, 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-0.5/fwupdate.spec.in000066400000000000000000000077341262311657600163210ustar00rootroot00000000000000%global efivar_version 0.21-1 %global efibootmgr_version 0.12-1 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-devel BuildRequires: pesign BuildRequires: elfutils popt-devel git gettext pkgconfig BuildRequires: systemd ExclusiveArch: x86_64 %{ix86} aarch64 Source0: https://github.com/rhinstaller/fwupdate/releases/download/%{name}-%{version}/%{name}-%{version}.tar.bz2 %ifarch x86_64 %global efiarch x64 %endif %ifarch %{ix86} %global efiarch 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 %description libs Library to allow for the simple manipulation of UEFI firmware updates. Requires: shim Requires: %{name}-efi = %{version}-%{release} %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. %prep %setup -q -n %{name}-%{version} git init git config user.email "%{name}-owner@fedoraproject.org" git config user.name "Fedora Ninjas" git add . git commit -a -q -m "%{version} baseline." git am %{patches} - 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-0.5/include/000077500000000000000000000000001262311657600146315ustar00rootroot00000000000000fwupdate-0.5/include/Makefile000066400000000000000000000004661262311657600162770ustar00rootroot00000000000000TOPDIR ?= $(shell pwd)/.. include $(TOPDIR)/Make.version include $(TOPDIR)/Make.defaults all clean : install : $(INSTALL) -d -m 755 $(DESTDIR)$(DEBUGSOURCE)fwupdate-$(VERSION)/include $(foreach src,$(wildcard *.[chS]),$(INSTALL) -m 644 $(src) $(DESTDIR)$(DEBUGSOURCE)fwupdate-$(VERSION)/include/$(src) ;) fwupdate-0.5/include/fwup-efi.h000066400000000000000000000013761262311657600165330ustar00rootroot00000000000000/* * 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 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; uint8_t dp[sizeof(efidp_header)]; }; } __attribute__((__packed__)) update_info; #endif /* _FWUP_EFI_H */ fwupdate-0.5/linux/000077500000000000000000000000001262311657600143455ustar00rootroot00000000000000fwupdate-0.5/linux/.gitignore000066400000000000000000000000671262311657600163400ustar00rootroot00000000000000fwupdate lib*.so.* tester *.po cleanup.service cleanup fwupdate-0.5/linux/Makefile000066400000000000000000000066761262311657600160240ustar00rootroot00000000000000TOPDIR ?= $(shell pwd)/.. include $(TOPDIR)/Make.version include $(TOPDIR)/Make.defaults LIB_LIBS= pthread BIN_LIBS= popt pthread PKLIBS = efivar efiboot CFLAGS ?= -g -O0 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 BUILDFLAGS := $(CFLAGS) -Wall -Wno-unused-result -Wno-unused-function \ -Wsign-compare -Werror=sign-compare \ -fshort-wchar --std=gnu11 \ -DLOCALEDIR=\"$(localedir)\" -D_GNU_SOURCE \ -DFWUP_EFI_DIR_NAME=\"$(EFIDIR)\" \ -I$(TOPDIR)/linux/include -iquote$(TOPDIR)/include/ \ $(foreach pklib,$(PKLIBS), $(shell pkg-config --cflags $(pklib))) \ $(PJONES) BINTARGETS=fwupdate INCTARGETS=include/fwup.h LIBTARGETS=libfwup.so.$(VERSION) libfwup.so.$(MAJOR_VERSION) PCTARGETS=fwup.pc POTARGETS = fwupdate.po libfwup.po TARGETS=$(BINTARGETS) $(INCTARGETS) $(LIBTARGETS) $(PCTARGETS) $(POTARGETS) libfwup.so cleanup.service cleanup .SUFFIXES: all : $(TARGETS) fwupdate : libfwup.so % : %.o $(CC) $(BUILDFLAGS) -o $@ $(patsubst lib%.so,-l%,$^) $(BIN_CCLDFLAGS) %.so.$(VERSION) : %.o $(CC) $(BUILDFLAGS) \ -Wl,-soname,$(patsubst %.o,%.so.$(MAJOR_VERSION),$<) \ -o $@ $^ $(LIB_CCLDFLAGS) %.so.$(MAJOR_VERSION) : %.so.$(VERSION) ln -sf $< $@ %.so : %.so.$(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,@@VERSION@@,$(VERSION),g" \ -e "s,@@DATADIR@@,$(datadir),g" \ -e "s,@@EFIDIR@@,$(EFIDIR),g" \ -e "s,@@LIBDIR@@,$(libdir),g" \ -e "s,@@LIBEXECDIR@@,$(libexecdir),g" \ $< > $@ tester : tester.c $(CC) -Wall -Werror -ggdb -L. -I./include -I/usr/include/efivar \ -lefivar -lfwup -o $@ $^ test : tester LD_LIBRARY_PATH=$(shell pwd) ./tester clean : @rm -vf $(BINTARGETS) $(LIBTARGETS) $(PCTARGETS) $(POTARGETS) *.o @rm -vf libfwup.so* @rm -vf cleanup.service cleanup install : all $(INSTALL) -d -m 755 $(DESTDIR)/$(libdir) $(foreach x, $(LIBTARGETS), $(INSTALL) -m 755 $(x) $(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.$(VERSION)), ln -fs $(x) $(patsubst %.so.$(VERSION),%.so,$(DESTDIR)/$(libdir)/$(x)); ln -fs $(x) $(patsubst %.so.$(VERSION),%.so.$(MAJOR_VERSION),$(DESTDIR)/$(libdir)/$(x));) $(INSTALL) -d -m 755 $(DESTDIR)$(datadir)/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)/usr/share/bash-completion/completions/ $(INSTALL) -m 755 bash-completion \ $(DESTDIR)/usr/share/bash-completion/completions/fwupdate fwupdate-0.5/linux/bash-completion000077500000000000000000000006451262311657600173640ustar00rootroot00000000000000#!/bin/bash _fwupdate() { local cur=${COMP_WORDS[COMP_CWORD]} if [[ "$cur" == -* ]]; then COMPREPLY=( $( compgen -W "-a -l" -- $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-0.5/linux/cleanup.in000077500000000000000000000011771262311657600163350ustar00rootroot00000000000000#!/bin/sh set -e if [ -e "@@DATADIR@@fwupdate/done" ]; then exit 0 fi 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 /boot/efi/EFI/@@EFIDIR@@/fw/fwupdate-* ; do if [ "${x}" != "/boot/efi/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 rm -f "${x}" fi done touch "@@DATADIR@@/fwupdate/done" fwupdate-0.5/linux/cleanup.service.in000066400000000000000000000001721262311657600177630ustar00rootroot00000000000000[Unit] Description=fwupdate housekeeping [Service] PrivateTmp=true Type=oneshot ExecStart=@@LIBEXECDIR@@fwupdate/cleanup fwupdate-0.5/linux/fwup.pc.in000066400000000000000000000003301262311657600162530ustar00rootroot00000000000000prefix=/usr exec_prefix=/usr libdir=@@LIBDIR@@ includedir=/usr/include Name: fwup Description: Library for deployment of UEFI firmware updates. Version: @@VERSION@@ Requires: efivar >= 0.16 Libs: -L${libdir} -lfwup fwupdate-0.5/linux/fwupdate.c000066400000000000000000000110471262311657600163330ustar00rootroot00000000000000/* * fwupdate.c - apply firmware updates * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 int print_system_resources(void) { fwup_resource_iter *iter; int rc; rc = fwup_resource_iter_create(&iter); if (rc < 0) { if (errno != ENOENT) warn(_("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; fwup_get_guid(re, &guid); rc = efi_guid_to_id_guid(guid, &id_guid); if (rc < 0) return -1; fwup_get_fw_version(re, &vers); fwup_get_lowest_supported_fw_version(re, &lowest); printf(_("%s version %d can be updated to any version above %d\n"), id_guid, vers, lowest-1); free(id_guid); } fwup_resource_iter_destroy(&iter); if (rc < 0) return -1; return 0; } #define ACTION_APPLY 0x01 #define ACTION_LIST 0x02 #define ACTION_SUPPORTED 0x04 int main(int argc, char *argv[]) { int action = 0; int quiet = 0; const char *guidstr = NULL; const char *filename = NULL; efi_guid_t guid; setlocale(LC_ALL, ""); bindtextdomain("fwupdate", LOCALEDIR); textdomain("fwupdate"); struct poptOption options[] = { {NULL, '\0', POPT_ARG_INTL_DOMAIN, "fwupdate" }, {"apply", 'a', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, ACTION_APPLY, _("Apply firmware updates"), " "}, {"list", 'l', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, ACTION_LIST, _("List supported firmware updates"), NULL}, {"supported", 's', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, ACTION_SUPPORTED, _("Query for firmware update support"), NULL}, {"quiet", 'q', POPT_ARG_VAL, &quiet, 1, _("Work quietly"), NULL}, 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)) errx(1, _("poptReadDefaultConfig failed: %s: %s"), poptBadOption(optcon, 0), poptStrerror(rc)); while ((rc = poptGetNextOpt(optcon)) > 0) ; if (action & ACTION_APPLY) { int rc; guidstr = poptGetArg(optcon); if (!guidstr) { warnx(_("missing argument: %s"), "guid"); poptPrintUsage(optcon, stderr, 0); exit(1); } rc = efi_str_to_guid(guidstr, &guid); if (rc < 0) errx(1, _("Invalid guid: \"%s\""), guidstr); filename = poptGetArg(optcon); if (!filename) { warnx(_("missing argument: %s"), "filename.cap"); poptPrintUsage(optcon, stderr, 0); exit(1); } } if (rc < -1) errx(2, _("invalid argument: \"%s\": %s"), poptBadOption(optcon, 0), poptStrerror(rc)); if (poptPeekArg(optcon)) errx(3, _("invalid argument: \"%s\""), poptPeekArg(optcon)); if (!action) { warnx(_("no action specified")); poptPrintUsage(optcon, stderr, 0); exit(4); } poptFreeContext(optcon); if (action & ACTION_SUPPORTED) { rc = fwup_supported(); if (rc == 0) { if (!quiet) printf("%s", _("Firmware updates are not supported on this machine.\n")); return 1; } else if (rc == 1) { if (!quiet) printf("%s", _("Firmware updates are supported on this machine.\n")); return 0; } } else if (action & ACTION_LIST) { rc = print_system_resources(); if (rc < 0 && errno != ENOENT) errx(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; while (1) { rc = fwup_resource_iter_next(iter, &re); if (rc < 0) err(2, _("Could not iterate resources")); if (rc == 0) break; efi_guid_t *tmpguid = NULL; fwup_get_guid(re, &tmpguid); if (!efi_guid_cmp(tmpguid, &guid)) { int fd = open(filename, O_RDONLY); if (fd < 0) err(2, _("could not open \"%s\""), filename); rc = fwup_set_up_update(re, 0, fd); if (rc < 0) err(2, _("Could not set up firmware update")); fwup_resource_iter_destroy(&iter); exit(0); } } errx(2, _("firmware resource not found")); } return 0; } fwupdate-0.5/linux/include/000077500000000000000000000000001262311657600157705ustar00rootroot00000000000000fwupdate-0.5/linux/include/fwup.h000066400000000000000000000041321262311657600171220ustar00rootroot00000000000000/* * libfw - library interface to apply firmware updates * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #ifndef LIBFW_H #define LIBFW_H #include #include #include #include #include extern int fwup_supported(void); #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 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 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_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 const char *fwup_last_attempt_status_to_string (uint64_t status); #endif /* LIBFW_H */ fwupdate-0.5/linux/libfwup.c000066400000000000000000000572051262311657600161720ustar00rootroot00000000000000/* * libfw - library interface to apply firmware updates * * Copyright 2015 Red Hat, Inc. * * See "COPYING" for license terms. * * Author: Peter Jones */ #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 char *arch_names_32[] = { #if defined(__x86_64__) || defined(__i386__) || defined(__i686__) "ia32", #endif "" }; 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 "" }; 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 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; } int fwup_supported(void) { struct stat buf; int rc; rc = stat(get_esrt_dir(1), &buf); if (rc < 0) return 0; if (buf.st_nlink < 3) return 0; return 1; } 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); } } #define FWUPDATE_GUID EFI_GUID(0x0abba7dc,0xe516,0x4167,0xbbf5,0x4d,0x9d,0x1c,0x73,0x94,0x16) 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) return -1; guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, hw_inst); if (rc < 0) 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) return -1; local = calloc(1, sizeof (*local)); if (!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) { 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) { rc = sz; 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) return -1; return get_info(guid, hw_inst, info); } local = (update_info *)data; if (local->update_info_version != UPDATE_INFO_VERSION) goto get_err; ssize_t sz = efidp_size((efidp)local->dp); if (sz < 0) { free(data); errno = EINVAL; return -1; } efidp_header *dp = malloc((size_t)sz); if (!dp) { free(data); errno = ENOMEM; return -1; } memcpy(dp, local->dp, (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) { err: return rc; } guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, info->hw_inst); if (rc < 0) 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)) { errno = EINVAL; return -1; } /* Make sure sizeof(*info) + dps won't integer overflow */ if ((size_t)dps > SSIZE_MAX - sizeof(*info)) { 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, 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; free(info2); errno = error; return rc; } typedef struct fwup_resource_s { esre esre; update_info *info; } fwup_resource; typedef struct fwup_resource_iter_s { DIR *dir; int dirfd; fwup_resource re; } fwup_resource_iter; int fwup_resource_iter_create(fwup_resource_iter **iter) { int error; if (!iter) { errno = EINVAL; return -1; } fwup_resource_iter *new = calloc(1, sizeof (fwup_resource_iter)); if (!new) { errno = ENOMEM; return -1; } new->dir = opendir(get_esrt_dir(1)); if (!new->dir) { err: error = errno; free(new); errno = error; return -1; } new->dirfd = dirfd(new->dir); if (new->dirfd < 0) goto err; *iter = new; return 0; } static void clear_res(fwup_resource *res) { if (res->info) { if (res->info->dp_ptr) free(res->info->dp_ptr); free(res->info); } memset(res, 0, sizeof (*res)); } int fwup_resource_iter_destroy(fwup_resource_iter **iterp) { if (!iterp) { 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; } 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) return -1; 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) { 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) { 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) return rc; res->info->capsule_flags = res->esre.capsule_flags; *re = res; return 1; } int fwup_clear_status(fwup_resource *re) { if (!re) { errno = EINVAL; return -1; } int rc; re->info->status = 0; rc = put_info(re->info); return rc; } int fwup_get_guid(fwup_resource *re, efi_guid_t **guid) { if (!re || !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) { 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) { 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) { 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) { 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) { errno = EINVAL; return -1; } if (!re->info->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; char shim_fs_path_tmpl[] = "/boot/efi/EFI/"FWUP_EFI_DIR_NAME"/shim"; char fwup_fs_path_tmpl[] = "/boot/efi/EFI/"FWUP_EFI_DIR_NAME"/fwup"; 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; 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; int rc; *shim_fs_path = NULL; *fwup_fs_path = NULL; *fwup_esp_path = NULL; i = 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) { 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) goto out; if (shim_fs_path_tmp) { *shim_fs_path = strdup(shim_fs_path_tmp); if (!*shim_fs_path) goto out; } if (fwup_fs_path_tmp) { *fwup_fs_path = strdup(fwup_fs_path_tmp); if (!*fwup_fs_path) goto out; } if (fwup_esp_path_tmp) *fwup_esp_path = fwup_esp_path_tmp; return 0; out: 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 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; rc = get_paths(&shim_fs_path, &fwup_fs_path, &fwup_esp_path); if (rc < 0) return -1; 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) goto out; dp_size=sz; dp_buf = calloc(1, dp_size); if (!dp_buf) goto out; if (!use_fwup_path) { loader_str = utf8_to_ucs2((uint8_t *)fwup_esp_path, -1); loader_sz = ucs2len(loader_str, -1) * 2; if (loader_sz) 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) goto out; uint8_t *opt=NULL; ssize_t opt_size=0; uint32_t attributes = LOAD_OPTION_ACTIVE; rc = asprintf(&label, "Linux-Firmware-Updater %s", fwup_esp_path); if (rc < 0) 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) goto out; opt = calloc(1, sz); if (!opt) 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) 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) 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) continue; loadopt = (efi_load_option *)var_data; if (!efi_loadopt_is_valid(loadopt, var_data_size)) { do_next: free(var_data); continue; } sz = efi_loadopt_pathlen(loadopt); if (sz != efidp_size((efidp)dp_buf)) goto do_next; efidp found_dp = efi_loadopt_path(loadopt); if (memcmp(found_dp, dp_buf, sz)) goto do_next; if ((ssize_t)var_data_size != opt_size) goto do_next; if (memcmp(loadopt, opt, opt_size)) goto do_next; if (memcmp(loadopt, opt, opt_size)) goto do_next; found = 1; boot_next = entry; break; } if (rc < 0) goto out; if (found) { efi_loadopt_attr_set(loadopt, LOAD_OPTION_ACTIVE); rc = efi_set_variable(*guid, name, var_data, var_data_size, attr); free(var_data); if (rc < 0) 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) goto out; sprintf(boot_next_name, "Boot%04X", boot_next); 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); if (rc < 0) goto out; } 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); 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, "/boot/efi%s", relpath); if (rc < 0) fullpath = NULL; out: free(relpath); return fullpath; } /** * 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 *fullpath = NULL; int fd = -1; int rc; /* 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 */ fullpath = get_existing_media_path (info); if (fullpath) { fd = open(fullpath, O_CREAT|O_TRUNC|O_CLOEXEC|O_RDWR, 0600); if (fd < 0) { warn("open of %s failed", fullpath); goto out; } } else { /* fall back to creating a new file from scratch */ rc = asprintf(&fullpath, "/boot/efi/EFI/%s/fw/fwupdate-XXXXXX.cap", FWUP_EFI_DIR_NAME); if (rc < 0) { warn("asprintf failed"); goto out; } fd = mkostemps(fullpath, 4, O_CREAT|O_TRUNC|O_CLOEXEC); if (fd < 0) { warn("mkostemps(%s) failed", fullpath); goto out; } } /* success, so take ownership of the string */ if (path) { *path = fullpath; fullpath = NULL; } out: 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; } /** * 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, 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 (infd < 0) { warn("fd invalid.\n"); return -1; } offset = lseek(infd, 0, SEEK_CUR); /* get device */ rc = get_info(&re->esre.guid, 0, &info); if (rc < 0) { warn("get_info failed.\n"); goto out; } /* get destination */ outfd = get_fd_and_media_path(info, &path); if (outfd < 0) { rc = -1; goto out; } fin = fdopen(infd, "r"); if (!fin) goto out; fout = fdopen(outfd, "w"); if (!fout) goto out; /* 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)) { warn("read failed"); rc = -1; goto out; } else { warnx("fgetc() == EOF but no error is set."); errno = EINVAL; rc = -1; goto out; } } rc = fputc(c, fout); if (rc == EOF) { if (feof(fout)) { break; } else if (ferror(fout)) { warn("write failed"); rc = -1; goto out; } else { warnx("fputc() == EOF but no error is set."); errno = EINVAL; rc = -1; 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) { warn("put_info failed.\n"); goto out; } /* update the firmware before the bootloader runs */ rc = set_up_boot_next(); if (rc < 0) goto out; out: error = errno; lseek(infd, offset, SEEK_SET); if (path) free(path); if (fin) fclose(fin); if (fout) fclose(fout); free_info(info); if (outfd >= 0) 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, const void *buf, size_t sz) { char *path = NULL; int fd = -1; int rc; update_info *info = NULL; int error; off_t off = 0; /* check parameters */ if (buf == NULL || sz == 0) { warn("buf invalid.\n"); rc = -1; goto out; } /* get device */ rc = get_info(&re->esre.guid, 0, &info); if (rc < 0) { warn("get_info failed.\n"); goto out; } /* get destination */ fd = get_fd_and_media_path(info, &path); if (fd < 0) { rc = -1; goto out; } /* write the buf to a new file */ while (sz-off) { ssize_t wsz; wsz = write(fd, buf+off, sz-off); if (wsz < 0 && (errno == EAGAIN || errno == EINTR)) continue; if (wsz < 0) { rc = wsz; warn("write failed"); goto out; } off += wsz; } /* 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) { warn("put_info failed.\n"); goto out; } /* update the firmware before the bootloader runs */ rc = set_up_boot_next(); if (rc < 0) goto out; out: error = errno; free_info(info); 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; } fwupdate-0.5/linux/sys/000077500000000000000000000000001262311657600151635ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/000077500000000000000000000000001262311657600167775ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/000077500000000000000000000000001262311657600175425ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/esrt/000077500000000000000000000000001262311657600205175ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/000077500000000000000000000000001262311657600221705ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/000077500000000000000000000000001262311657600234115ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/capsule_flags000066400000000000000000000000041262311657600261360ustar00rootroot000000000000000x1 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/fw_class000066400000000000000000000000451262311657600251340ustar00rootroot00000000000000819b858e-c52c-402f-80e1-5b311b6c1959 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/fw_type000066400000000000000000000000021262311657600250010ustar00rootroot000000000000001 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/fw_version000066400000000000000000000000021262311657600255050ustar00rootroot000000000000001 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/last_attempt_status000066400000000000000000000000021262311657600274300ustar00rootroot000000000000000 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/last_attempt_version000066400000000000000000000000021262311657600275720ustar00rootroot000000000000001 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry0/lowest_supported_fw_version000066400000000000000000000000021262311657600312070ustar00rootroot000000000000001 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/000077500000000000000000000000001262311657600234125ustar00rootroot00000000000000fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/capsule_flags000066400000000000000000000000041262311657600261370ustar00rootroot000000000000000x1 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/fw_class000066400000000000000000000000451262311657600251350ustar00rootroot0000000000000095e0eca8-a73d-449f-befc-d648692bae52 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/fw_type000066400000000000000000000000021262311657600250020ustar00rootroot000000000000001 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/fw_version000066400000000000000000000000021262311657600255060ustar00rootroot000000000000004 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/last_attempt_status000066400000000000000000000000021262311657600274310ustar00rootroot000000000000000 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/last_attempt_version000066400000000000000000000000021262311657600275730ustar00rootroot000000000000000 fwupdate-0.5/linux/sys/firmware/efi/esrt/entries/entry1/lowest_supported_fw_version000066400000000000000000000000021262311657600312100ustar00rootroot000000000000004 fwupdate-0.5/linux/sys/firmware/efi/esrt/fw_resource_count000066400000000000000000000000021262311657600241650ustar00rootroot000000000000002 fwupdate-0.5/linux/sys/firmware/efi/esrt/fw_resource_count_max000066400000000000000000000000021262311657600250320ustar00rootroot000000000000002 fwupdate-0.5/linux/sys/firmware/efi/esrt/fw_resource_version000066400000000000000000000000021262311657600245220ustar00rootroot000000000000001 fwupdate-0.5/linux/tester.c000066400000000000000000000010221262311657600160120ustar00rootroot00000000000000 #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-0.5/linux/ucs2.h000066400000000000000000000072441262311657600154010ustar00rootroot00000000000000#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 const *chars, size_t max) { size_t i, j; char *ret = alloca(max * 6 + 1); if (!ret) return NULL; memset(ret, 0, max * 6 +1); for (i=0, j=0; chars[i] && i < max; 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-0.5/linux/util.h000066400000000000000000000114111262311657600154710ustar00rootroot00000000000000/* * 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) static inline int __attribute__((unused)) read_file_at(int dfd, char *name, uint8_t **buf, size_t *bufsize) { int saved_errno = 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; } #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; #endif /* LIBFW_UTIL_H */