diff --git a/alpine/.gitignore b/alpine/.gitignore index fd95f3b16..772426b56 100644 --- a/alpine/.gitignore +++ b/alpine/.gitignore @@ -1,5 +1,8 @@ *.img *.img.gz -mobylinux.iso +/mobylinux-bios.iso +/mobylinux-efi.iso +/mobylinux-boot.vhdx +/mobylinux.efi Dockerfile.armhf etc/inittab diff --git a/alpine/Dockerfile b/alpine/Dockerfile index 10a8f16a2..cabab1189 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -63,6 +63,7 @@ COPY packages/nc-vsock/nc-vsock /usr/bin COPY packages/vsudd/vsudd /sbin COPY packages/vsudd/etc /etc COPY packages/mobyconfig/mobyconfig /usr/bin +COPY packages/gummiboot/gummiboot.tar.gz /usr/share/src/ RUN \ rc-update add swap boot && \ diff --git a/alpine/Dockerfile.iso b/alpine/Dockerfile.bios similarity index 83% rename from alpine/Dockerfile.iso rename to alpine/Dockerfile.bios index a9eb38fdb..07daee20c 100644 --- a/alpine/Dockerfile.iso +++ b/alpine/Dockerfile.bios @@ -1,3 +1,4 @@ +# Create a legacy BIOS bootable ISO FROM ubuntu:15.10 RUN apt-get update && apt-get -y upgrade && apt-get -y install \ @@ -16,9 +17,9 @@ COPY kernel/vmlinuz64 /tmp/iso COPY isolinux.cfg /tmp/iso/isolinux RUN cd /tmp/iso && \ - genisoimage -o ../output.iso -l -J -R \ + genisoimage -o ../mobylinux-bios.iso -l -J -R \ -c isolinux/boot.cat \ -b isolinux/isolinux.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -V MobyLinux . && \ - isohybrid ../output.iso + isohybrid ../mobylinux-bios.iso diff --git a/alpine/Dockerfile.efi b/alpine/Dockerfile.efi new file mode 100644 index 000000000..6f1140634 --- /dev/null +++ b/alpine/Dockerfile.efi @@ -0,0 +1,55 @@ +# Create a EFI Bootable ISO +FROM ubuntu:15.10 + +RUN apt-get update && apt-get -y upgrade && apt-get -y install \ + binutils mtools dosfstools xorriso \ + gdisk qemu + +RUN mkdir -p /tmp/efi + +COPY initrd.img.gz /tmp/efi +COPY kernel/vmlinuz64 /tmp/efi +COPY packages/gummiboot/linuxx64.efi.stub /tmp/efi + +# Create a EFI boot file with kernel and initrd. From: +# https://github.com/haraldh/mkrescue-uefi/blob/master/mkrescue-uefi.sh +RUN cd /tmp/efi && \ + echo "earlyprintk=serial console=ttyS0" > cmdline.txt && \ + objcopy \ + --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \ + --add-section .cmdline=./cmdline.txt --change-section-vma .cmdline=0x30000 \ + --add-section .linux=./vmlinuz64 --change-section-vma .linux=0x40000 \ + --add-section .initrd=initrd.img.gz --change-section-vma .initrd=0x3000000 \ + ./linuxx64.efi.stub \ + mobylinux.efi + +# create a ISO with a EFI boot partition +RUN cd /tmp/efi && \ + mkdir -p iso && \ + dd if=/dev/zero of=iso/efi.raw bs=1024 count=70000 && \ + mkfs.vfat iso/efi.raw && \ + mmd -i iso/efi.raw ::/EFI && \ + mmd -i iso/efi.raw ::/EFI/BOOT && \ + mcopy -i iso/efi.raw mobylinux.efi ::/EFI/BOOT/BOOTX64.EFI && \ + xorriso -as mkisofs \ + -R -f -e efi.raw -no-emul-boot -o mobylinux-efi.iso iso + +# How to build a VHDX. Commented out because we are currently not using it +# Don't delete: It took too long to figure out how to do this... +# # create a disk image (150MB) +# # This is a little odd, as we run this as part of the default command. +# # Can't run this during the build step as it requires privilege. +# # The Magic numbers in losetup are startsector (2048) times 512 and +# # (endsector - startsector) * 512 +# CMD cd /tmp/efi && \ +# dd if=/dev/zero of=disk.raw bs=1024 count=51200 && \ +# sgdisk -N 1 -t 1:ef00 disk.raw && \ +# losetup -o 1048576 --sizelimit 51362816 /dev/loop/1 disk.raw && \ +# mkfs.vfat /dev/loop/1 && \ +# echo "drive c: file=\"/dev/loop/1\" mtools_skip_check=1" > /etc/mtools.conf && \ +# mmd c:/EFI && \ +# mmd c:/EFI/BOOT && \ +# mcopy mobylinux.efi c:/EFI/BOOT/BOOTX64.EFI && \ +# losetup -d /dev/loop/1 && \ +# qemu-img convert -O vhdx disk.raw mobylinux-boot.vhdx && \ +# cp /tmp/efi/mobylinux.efi /tmp/efi/mobylinuxefi.iso /tmp/efi/mobylinux-boot.vhdx /mnt/ diff --git a/alpine/Makefile b/alpine/Makefile index 0da4b03ea..a59cfcfd6 100644 --- a/alpine/Makefile +++ b/alpine/Makefile @@ -1,4 +1,4 @@ -all: initrd.img.gz +all: initrd.img.gz mobylinux-efi.iso ETCFILES=etc/issue etc/motd etc/network/interfaces ETCFILES+=etc/securetty @@ -14,9 +14,15 @@ initrd.img: Dockerfile mkinitrd.sh repositories $(ETCFILES) initrd.img.gz: initrd.img cat initrd.img | gzip -9 > initrd.img.gz -mobylinux.iso: initrd.img Dockerfile.iso isolinux.cfg - docker-compose build iso - docker-compose run --rm -T iso cp /tmp/output.iso /mnt/mobylinux.iso +mobylinux-efi.iso: initrd.img.gz Dockerfile.efi + docker-compose build efi + docker-compose run --rm -T efi \ + cp /tmp/efi/mobylinux.efi /tmp/efi/mobylinux-efi.iso /mnt + +mobylinux-bios.iso: initrd.img Dockerfile.bios isolinux.cfg + docker-compose build bios + docker-compose run --rm -T bios \ + cp /tmp/mobylinux-bios.iso /mnt arm: initrd-arm.img @@ -37,6 +43,6 @@ initrd-arm.img: Dockerfile.armhf clean: rm -f initrd.img initrd.img.gz initrd-arm.img Dockerfile.armhf etc/inittab - rm -f mobylinux.iso + rm -f mobylinux-bios.iso mobylinux-efi.iso mobylinux.efi $(MAKE) -C packages clean $(MAKE) -C kernel clean diff --git a/alpine/docker-compose.yml b/alpine/docker-compose.yml index e338bc84d..4707fd4e2 100644 --- a/alpine/docker-compose.yml +++ b/alpine/docker-compose.yml @@ -5,16 +5,24 @@ services: context: . network_mode: bridge volumes: - - .:/mnt + - .:/mnt + efi: + privileged: true + build: + context: . + dockerfile: Dockerfile.efi + network_mode: bridge + volumes: + - .:/mnt + bios: + build: + context: . + dockerfile: Dockerfile.bios + network_mode: bridge + volumes: + - .:/mnt arm: build: context: . dockerfile: Dockerfile.armhf network_mode: bridge - iso: - build: - context: . - dockerfile: Dockerfile.iso - network_mode: bridge - volumes: - - .:/mnt diff --git a/alpine/packages/Makefile b/alpine/packages/Makefile index 8b1d41a27..f54668e57 100644 --- a/alpine/packages/Makefile +++ b/alpine/packages/Makefile @@ -9,6 +9,7 @@ all: $(MAKE) -C nc-vsock OS=linux $(MAKE) -C vsudd OS=linux $(MAKE) -C llmnrd OS=linux + $(MAKE) -C gummiboot OS=linux arm: $(MAKE) -C proxy OS=linux ARCH=arm @@ -31,3 +32,4 @@ clean: $(MAKE) -C nc-vsock clean $(MAKE) -C vsudd clean $(MAKE) -C llmnrd clean + $(MAKE) -C gummiboot clean diff --git a/alpine/packages/gummiboot/.gitignore b/alpine/packages/gummiboot/.gitignore new file mode 100644 index 000000000..7f70c31e8 --- /dev/null +++ b/alpine/packages/gummiboot/.gitignore @@ -0,0 +1,2 @@ +/gummiboot.tar.gz +/linuxx64.efi.stub \ No newline at end of file diff --git a/alpine/packages/gummiboot/Dockerfile b/alpine/packages/gummiboot/Dockerfile new file mode 100644 index 000000000..ad81d7d46 --- /dev/null +++ b/alpine/packages/gummiboot/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:15.10 + +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get -y install build-essential automake pkg-config libtool gnu-efi + +RUN mkdir -p /gummiboot + +COPY gummiboot /gummiboot +RUN tar czvf /gummiboot.tar.gz gummiboot + +WORKDIR /gummiboot + +RUN ./autogen.sh && \ + ./configure && \ + make linuxx64.efi.stub diff --git a/alpine/packages/gummiboot/Makefile b/alpine/packages/gummiboot/Makefile new file mode 100644 index 000000000..0515f286c --- /dev/null +++ b/alpine/packages/gummiboot/Makefile @@ -0,0 +1,11 @@ +all: linuxx64.efi.stub + +linuxx64.efi.stub: Dockerfile gummiboot/* + docker build -t gummiboot:build . + docker run --rm gummiboot:build cat /gummiboot.tar.gz > gummiboot.tar.gz + docker run --rm gummiboot:build \ + cat /gummiboot/linuxx64.efi.stub > linuxx64.efi.stub + +clean: + rm -f linuxx64.efi.stub gummiboot.tar.gz + docker images -q gummiboot:build | xargs docker rmi -f diff --git a/alpine/packages/gummiboot/gummiboot/LICENSE b/alpine/packages/gummiboot/gummiboot/LICENSE new file mode 100644 index 000000000..4362b4915 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/alpine/packages/gummiboot/gummiboot/Makefile.am b/alpine/packages/gummiboot/gummiboot/Makefile.am new file mode 100644 index 000000000..6568a355e --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/Makefile.am @@ -0,0 +1,203 @@ +# +# This file is part of gummiboot +# +# Copyright (C) 2013 Karel Zak +# +# gummiboot is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# gummiboot 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +AM_MAKEFLAGS = --no-print-directory + +gummibootlibdir = $(prefix)/lib/gummiboot + +AM_CPPFLAGS = -include config.h +AM_CFLAGS = \ + -D_GNU_SOURCE \ + -Wall \ + -Wextra \ + -Wmissing-prototypes \ + -Wno-unused-parameter +AM_LDFLAGS = + +EXTRA_DIST = autogen.sh README LICENSE +CLEANFILES = + +# ------------------------------------------------------------------------------ +if HAVE_BLKID +bin_PROGRAMS = gummiboot + +gummiboot_SOURCES = \ + src/setup/setup.c \ + src/setup/efivars.c \ + src/setup/efivars.h + +gummiboot_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DMACHINE_TYPE_NAME=\"$(MACHINE_TYPE_NAME)\" \ + -DGUMMIBOOTLIBDIR=\"$(gummibootlibdir)\" + +gummiboot_CFLAGS = \ + $(AM_CFLAGS) \ + $(BLKID_CFLAGS) + +gummiboot_LDADD = \ + $(BLKID_LIBS) +endif + +if ENABLE_MANPAGES +%.8: %.xml + $(AM_V_GEN)$(XSLTPROC) -o $@ --nonet \ + --stringparam man.output.quietly 1 \ + --stringparam man.th.extra1.suppress 1 \ + --stringparam man.authors.section.enabled 0 \ + --stringparam man.copyright.section.enabled 0 \ + http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + +dist_man_MANS = man/gummiboot.8 +endif + +EXTRA_DIST += man/gummiboot.xml +CLEANFILES += man/gummiboot.8 + +# ------------------------------------------------------------------------------ +# EFI compilation -- this part of the build system uses custom make rules and +# bypasses regular automake to provide absolute control on compiler and linker +# flags. +efi_cppflags = \ + $(EFI_CPPFLAGS) \ + -I$(top_builddir) -include config.h \ + -I$(EFI_INC_DIR)/efi \ + -I$(EFI_INC_DIR)/efi/$(ARCH) \ + -DMACHINE_TYPE_NAME=\"$(MACHINE_TYPE_NAME)\" + +efi_cflags = \ + $(EFI_CFLAGS) \ + -Wall \ + -Wextra \ + -std=gnu90 \ + -nostdinc \ + -ggdb -O0 \ + -fpic \ + -fshort-wchar \ + -nostdinc \ + -ffreestanding \ + -fno-strict-aliasing \ + -fno-stack-protector \ + -Wsign-compare \ + -mno-sse \ + -mno-mmx + +if ARCH_X86_64 +efi_cflags += \ + -mno-red-zone \ + -DEFI_FUNCTION_WRAPPER \ + -DGNU_EFI_USE_MS_ABI +endif + +efi_ldflags = \ + $(EFI_LDFLAGS) \ + -T $(EFI_LDS_DIR)/elf_$(ARCH)_efi.lds \ + -shared \ + -Bsymbolic \ + -nostdlib \ + -znocombreloc \ + -L $(EFI_LIB_DIR) \ + $(EFI_LDS_DIR)/crt0-efi-$(ARCH).o + +# ------------------------------------------------------------------------------ +gummiboot_headers = \ + src/efi/util.h \ + src/efi/console.h \ + src/efi/graphics.h \ + src/efi/pefile.h + +gummiboot_sources = \ + src/efi/util.c \ + src/efi/console.c \ + src/efi/graphics.c \ + src/efi/pefile.c \ + src/efi/gummiboot.c + +gummiboot_objects = $(addprefix $(top_builddir)/,$(gummiboot_sources:.c=.o)) +gummiboot_solib = $(top_builddir)/src/efi/gummiboot.so +gummiboot = gummiboot$(MACHINE_TYPE_NAME).efi + +gummibootlib_DATA = $(gummiboot) +CLEANFILES += $(gummiboot_objects) $(gummiboot_solib) $(gummiboot) +EXTRA_DIST += $(gummiboot_sources) $(gummiboot_headers) + +$(top_builddir)/src/efi/%.o: $(top_srcdir)/src/efi/%.c $(addprefix $(top_srcdir)/,$(gummiboot_headers)) + @$(MKDIR_P) $(top_builddir)/src/efi/ + $(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@ + +$(gummiboot_solib): $(gummiboot_objects) + $(AM_V_CCLD)$(LD) $(efi_ldflags) $(gummiboot_objects) \ + -o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \ + nm -D -u $@ | grep ' U ' && exit 1 || : +.DELETE_ON_ERROR: $(gummboot_solib) + +$(gummiboot): $(gummiboot_solib) + $(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \ + -j .dynsym -j .rel -j .rela -j .reloc \ + --target=efi-app-$(ARCH) $< $@ + +# ------------------------------------------------------------------------------ +stub_headers = \ + src/efi/util.h \ + src/efi/pefile.h \ + src/efi/linux.h + +stub_sources = \ + src/efi/util.c \ + src/efi/pefile.c \ + src/efi/linux.c \ + src/efi/stub.c + +stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o)) +stub_solib = $(top_builddir)/src/efi/stub.so +stub = linux$(MACHINE_TYPE_NAME).efi.stub + +gummibootlib_DATA += $(stub) +CLEANFILES += $(stub_objects) $(stub_solib) $(stub) +EXTRA_DIST += $(stub_sources) $(stub_headers) + +$(top_builddir)/src/efi/%.o: $(top_srcdir)/src/efi/%.c $(addprefix $(top_srcdir)/,$(stub_headers)) + @$(MKDIR_P) $(top_builddir)/src/efi/ + $(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@ + +$(stub_solib): $(stub_objects) + $(AM_V_CCLD)$(LD) $(efi_ldflags) $(stub_objects) \ + -o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \ + nm -D -u $@ | grep ' U ' && exit 1 || : +.DELETE_ON_ERROR: $(gummboot_solib) + +$(stub): $(stub_solib) + $(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \ + -j .dynsym -j .rel -j .rela -j .reloc \ + --target=efi-app-$(ARCH) $< $@ + +# ------------------------------------------------------------------------------ +CLEANFILES += test-disk.img +EXTRA_DIST += test/test-create-disk.sh + +test-disk.img: gummiboot$(MACHINE_TYPE_NAME).efi test/test-create-disk.sh + $(AM_V_GEN)test/test-create-disk.sh + +qemu: test-disk.img + $(QEMU) -machine accel=kvm -m 1024 -bios $(QEMU_BIOS) -snapshot test-disk.img + +install-tree: all + rm -rf $(abs_srcdir)/install-tree + $(MAKE) install DESTDIR=$(abs_srcdir)/install-tree + tree $(abs_srcdir)/install-tree diff --git a/alpine/packages/gummiboot/gummiboot/README b/alpine/packages/gummiboot/gummiboot/README new file mode 100644 index 000000000..09f835f9c --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/README @@ -0,0 +1,26 @@ +gummiboot Simple UEFI boot manager + +gummiboot executes EFI images. The default entry is selected by a configured +pattern (glob) or an on-screen menu. + +gummiboot operates on the EFI System Partition (ESP) only. Configuration +file fragments, kernels, initrds, other EFI images need to reside on the +ESP. Linux kernels must be built with CONFIG_EFI_STUB to be able to be +directly executed as an EFI image. + +gummiboot reads simple and entirely generic configurion files; one file +per boot entry to select from. + +Pressing Space (or most other) keys during bootup will show an on-screen +menu with all configured entries to select from. Pressing enter on the +selected entry loads and starts the EFI image. + +If no timeout is configured and no key pressed during bootup, the default +entry is booted right away. + +Further documentation is available in the gummiboot wiki at: + http://freedesktop.org/wiki/Software/gummiboot + +Links: + http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec + http://www.freedesktop.org/software/systemd/man/kernel-install.html diff --git a/alpine/packages/gummiboot/gummiboot/TODO b/alpine/packages/gummiboot/gummiboot/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/alpine/packages/gummiboot/gummiboot/autogen.sh b/alpine/packages/gummiboot/gummiboot/autogen.sh new file mode 100755 index 000000000..dfe9eba24 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/autogen.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# This file is part of gummiboot. +# +# gummiboot is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# gummiboot 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +set -e + +autoreconf --force --install --symlink + +args="\ +--prefix=/usr" + +if [ "x$1" = "xc" ]; then + ./configure $args + make clean +else + echo + echo "----------------------------------------------------------------" + echo "Initialized build system. For a common configuration please run:" + echo "----------------------------------------------------------------" + echo + echo "./configure $args" + echo +fi diff --git a/alpine/packages/gummiboot/gummiboot/configure.ac b/alpine/packages/gummiboot/gummiboot/configure.ac new file mode 100644 index 000000000..27bbe1d73 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/configure.ac @@ -0,0 +1,155 @@ +# +# This file is part of gummiboot. +# +# Copyright (C) 2013 Karel Zak +# +# gummiboot is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# gummiboot 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +AC_INIT([gummiboot], + [48], + [systemd-devel@lists.freedesktop.org], + [gummiboot], + [http://freedesktop.org/wiki/Software/gummiboot]) + +AC_CONFIG_SRCDIR([src/setup/setup.c]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_PREFIX_DEFAULT([/usr]) + +AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects]) +AM_SILENT_RULES([yes]) + +AC_CANONICAL_HOST +AC_SYS_LARGEFILE + +AC_PROG_CC + +dnl Don't try to use things like -std=c99 for efi compilation +EFI_CC=$CC +AC_SUBST([EFI_CC]) + +AC_PROG_CC_C99 +AM_PROG_CC_C_O +AC_PROG_GCC_TRADITIONAL + +AC_PROG_MKDIR_P +AC_PATH_PROG([XSLTPROC], [xsltproc]) + +dnl Define ARCH_ conditionals +SET_ARCH(IA32, i*86*) +SET_ARCH(X86_64, x86_64*) +SET_ARCH(IA64, ia64*) + +ARCH=`echo $host | sed "s/\(-\).*$//"` + +AM_COND_IF(ARCH_IA32, [ + ARCH=ia32 + MACHINE_TYPE_NAME=ia32]) + +AM_COND_IF(ARCH_X86_64, [ + MACHINE_TYPE_NAME=x64]) + +AC_SUBST([ARCH]) +AC_SUBST([MACHINE_TYPE_NAME]) + +# QEMU and OVMF UEFI firmware +AS_IF([test x"$cross_compiling" = "xyes"], [], [ + AC_PATH_PROG([QEMU], [qemu-system-x86_64]) + AC_CHECK_FILE([/usr/share/qemu/bios-ovmf.bin], [QEMU_BIOS=/usr/share/qemu/bios-ovmf.bin]) + AC_CHECK_FILE([/usr/share/qemu-ovmf/bios.bin], [QEMU_BIOS=/usr/share/qemu-ovmf/bios.bin]) + AC_SUBST([QEMU_BIOS]) +]) + +# ------------------------------------------------------------------------------ +dnl GNU EFI doesn't use relative paths: efi.h includes efibind.h which is in +dnl ${ARCH} relative to efi.h. I can't find a way to get AC_CHECK_HEADERS to +dnl add -I/usr/include/efi/${ARCH} to the conftest.c build. So, just test for +dnl efibind.h as the chances of efi.h not existing if it does are very low. +AC_CHECK_HEADER(efi/${ARCH}/efibind.h, [], + [AC_MSG_ERROR([*** GNU EFI header efibind.h not found])]) + +efiroot=$(echo $(cd /usr/lib/$(gcc -print-multi-os-directory); pwd)) +EFI_LIB_DIR="$efiroot" +AC_ARG_WITH(efi-libdir, + AS_HELP_STRING([--with-efi-libdir=PATH], [Path to efi lib directory]), + [EFI_LIB_DIR="$withval"], [EFI_LIB_DIR="$efiroot"] +) +AC_SUBST([EFI_LIB_DIR]) + +dnl extra objects and linker scripts +AC_ARG_WITH(efi-ldsdir, + AS_HELP_STRING([--with-efi-ldsdir=PATH], [Path to efi lds directory]), + [EFI_LDS_DIR="$withval"], + [ + for EFI_LDS_DIR in "${efiroot}/gnuefi" "${efiroot}"; do + for lds in ${EFI_LDS_DIR}/elf_${ARCH}_efi.lds; do + test -f ${lds} && break 2 + done + done + ] +) +AC_SUBST([EFI_LDS_DIR]) + +AC_ARG_WITH(efi-includedir, + AS_HELP_STRING([--with-efi-includedir=PATH], [Path to efi include directory]), + [EFI_INC_DIR="$withval"], [EFI_INC_DIR="/usr/include"] +) +AC_SUBST([EFI_INC_DIR]) + +# ------------------------------------------------------------------------------ +AC_ARG_ENABLE(blkid, AS_HELP_STRING([--disable-blkid], [disable blkid support])) +if test "x$enable_blkid" != "xno"; then + PKG_CHECK_MODULES(BLKID, [ blkid >= 2.20 ], + [AC_DEFINE(HAVE_BLKID, 1, [Define if blkid is available]) have_blkid=yes], have_blkid=no) + if test "x$have_blkid" = xno -a "x$enable_blkid" = xyes; then + AC_MSG_ERROR([*** blkid support requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_BLKID, [test "$have_blkid" = "yes"]) + +# ------------------------------------------------------------------------------ +have_manpages=no +AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable manpages])) +AS_IF([test "x$enable_manpages" != xno], [ + AS_IF([test "x$enable_manpages" = xyes -a "x$XSLTPROC" = x], [ + AC_MSG_ERROR([*** Manpages requested but xsltproc not found]) + ]) + AS_IF([test "x$XSLTPROC" != x], [have_manpages=yes]) +]) +AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"]) + +# ------------------------------------------------------------------------------ +AC_CONFIG_FILES([ + Makefile +]) + +AC_OUTPUT +AC_MSG_RESULT([ + $PACKAGE_NAME $VERSION + + prefix: ${prefix} + arch: $ARCH + EFI machine type: $MACHINE_TYPE_NAME + + EFI libdir: ${EFI_LIB_DIR} + EFI ldsdir: ${EFI_LDS_DIR} + EFI includedir: ${EFI_INC_DIR} + + blkid: ${have_blkid} + man pages: ${have_manpages} + + QEMU: ${QEMU} + QEMU OVMF: ${QEMU_BIOS} +]) diff --git a/alpine/packages/gummiboot/gummiboot/m4/arch.m4 b/alpine/packages/gummiboot/gummiboot/m4/arch.m4 new file mode 100644 index 000000000..f17b4278e --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/m4/arch.m4 @@ -0,0 +1,13 @@ + +dnl SET_ARCH(ARCHNAME, PATTERN) +dnl +dnl Define ARCH_ condition if the pattern match with the current +dnl architecture +dnl +AC_DEFUN([SET_ARCH], [ + cpu_$1=false + case "$host" in + $2) cpu_$1=true ;; + esac + AM_CONDITIONAL(AS_TR_CPP(ARCH_$1), [test "x$cpu_$1" = xtrue]) +]) diff --git a/alpine/packages/gummiboot/gummiboot/man/gummiboot.xml b/alpine/packages/gummiboot/gummiboot/man/gummiboot.xml new file mode 100644 index 000000000..36552f85d --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/man/gummiboot.xml @@ -0,0 +1,137 @@ + + + + + + + + gummiboot + gummiboot + + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + gummiboot + 8 + + + + gummiboot + Setup and manage Gummiboot Boot Manager + + + + + gummiboot OPTIONSstatus + + + gummiboot OPTIONSupdate + + + gummiboot OPTIONSinstall + + + gummiboot OPTIONSremove + + + + + Description + + gummiboot checks, updates, + installs or removes the boot loader from the current + system. + + gummiboot status checks and prints the + currently installed versions of the boot loader binaries and the + all current EFI boot variables. + + gummiboot update updates all installed + versions of gummiboot, if the current version is newer than the + version installed in the EFI system partition. This also includes + the EFI default/fallback loader at /EFI/Boot/boot*.efi. An + gummiboot entry in the EFI boot variables is created, if there + is no current entry. A created entry will be added to the end of + the boot order list. + + gummiboot install installs gummiboot into + the EFI system partition. A copy of gummiboot will be stored as + the EFI default/fallback loader at /EFI/Boot/boot*.efi. An gummiboot + entry in the EFI boot variables is created and added to the top + of the boot order list. + + gummiboot remove removes all installed + versions of gummiboot from the EFI system partition, and removes + gummiboot from the EFI boot variables. + + If no command is passed status is + implied. + + + + Options + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + Path to the EFI system + partition. The default is /boot. + + + + + Do not touch the EFI boot + variables. + + + + + + Exit status + On success 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + Gummiboot wiki + Boot loader specification + Systemd boot loader interface + bootctl1 + + + diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/console.c b/alpine/packages/gummiboot/gummiboot/src/efi/console.c new file mode 100644 index 000000000..6206c8031 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/console.c @@ -0,0 +1,141 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#include +#include + +#include "util.h" +#include "console.h" + +#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \ + { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } } + +struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; + +typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This; + BOOLEAN ExtendedVerification; +); + +typedef UINT8 EFI_KEY_TOGGLE_STATE; + +typedef struct { + UINT32 KeyShiftState; + EFI_KEY_TOGGLE_STATE KeyToggleState; +} EFI_KEY_STATE; + +typedef struct { + EFI_INPUT_KEY Key; + EFI_KEY_STATE KeyState; +} EFI_KEY_DATA; + +typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This; + EFI_KEY_DATA *KeyData; +); + +typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This; + EFI_KEY_TOGGLE_STATE *KeyToggleState; +); + +typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)( + EFI_KEY_DATA *KeyData; +); + +typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction; + VOID **NotifyHandle; +); + +typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This; + VOID *NotificationHandle; +); + +typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL { + EFI_INPUT_RESET_EX Reset; + EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx; + EFI_EVENT WaitForKeyEx; + EFI_SET_STATE SetState; + EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify; + EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify; +} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; + +EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { + EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; + static BOOLEAN checked; + UINTN index; + EFI_INPUT_KEY k; + EFI_STATUS err; + + if (!checked) { + err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx); + if (EFI_ERROR(err)) + TextInputEx = NULL; + + checked = TRUE; + } + + /* wait until key is pressed */ + if (wait) { + if (TextInputEx) + uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index); + else + uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index); + } + + if (TextInputEx) { + EFI_KEY_DATA keydata; + UINT64 keypress; + + err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata); + if (!EFI_ERROR(err)) { + UINT32 shift = 0; + + /* do not distinguish between left and right keys */ + if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) { + if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)) + shift |= EFI_CONTROL_PRESSED; + if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)) + shift |= EFI_ALT_PRESSED; + }; + + /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */ + keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar); + if (keypress > 0) { + *key = keypress; + return 0; + } + } + } + + /* fallback for firmware which does not support SimpleTextInputExProtocol + * + * This is also called in case ReadKeyStrokeEx did not return a key, because + * some broken firmwares offer SimpleTextInputExProtocol, but never acually + * handle any key. */ + err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k); + if (EFI_ERROR(err)) + return err; + + *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar); + return 0; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/console.h b/alpine/packages/gummiboot/gummiboot/src/efi/console.h new file mode 100644 index 000000000..8c2a31a5e --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/console.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#ifndef __GUMMIBOOT_CONSOLE_H +#define __GUMMIBOOT_CONSOLE_H + +#define EFI_SHIFT_STATE_VALID 0x80000000 +#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 +#define EFI_LEFT_CONTROL_PRESSED 0x00000008 +#define EFI_RIGHT_ALT_PRESSED 0x00000010 +#define EFI_LEFT_ALT_PRESSED 0x00000020 + +#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED) +#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED) +#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni)) +#define KEYCHAR(k) ((k) & 0xffff) +#define CHAR_CTRL(c) ((c) - 'a' + 1) + +EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait); +#endif diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/graphics.h b/alpine/packages/gummiboot/gummiboot/src/efi/graphics.h new file mode 100644 index 000000000..edec1f4aa --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/graphics.h @@ -0,0 +1,26 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + * Copyright (C) 2013 Intel Corporation + * Authored by Joonas Lahtinen + */ + +#ifndef __GUMMIBOOT_GRAPHICS_H +#define __GUMMIBOOT_GRAPHICS_H + +EFI_STATUS graphics_mode(BOOLEAN on); +EFI_STATUS graphics_splash(EFI_FILE *root_dir, CHAR16 *path, + const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background); +#endif diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/linux.c b/alpine/packages/gummiboot/gummiboot/src/efi/linux.c new file mode 100644 index 000000000..809c69310 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/linux.c @@ -0,0 +1,130 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "util.h" +#include "linux.h" + +#define SETUP_MAGIC 0x53726448 /* "HdrS" */ +struct SetupHeader { + UINT8 boot_sector[0x01f1]; + UINT8 setup_secs; + UINT16 root_flags; + UINT32 sys_size; + UINT16 ram_size; + UINT16 video_mode; + UINT16 root_dev; + UINT16 signature; + UINT16 jump; + UINT32 header; + UINT16 version; + UINT16 su_switch; + UINT16 setup_seg; + UINT16 start_sys; + UINT16 kernel_ver; + UINT8 loader_id; + UINT8 load_flags; + UINT16 movesize; + UINT32 code32_start; + UINT32 ramdisk_start; + UINT32 ramdisk_len; + UINT32 bootsect_kludge; + UINT16 heap_end; + UINT8 ext_loader_ver; + UINT8 ext_loader_type; + UINT32 cmd_line_ptr; + UINT32 ramdisk_max; + UINT32 kernel_alignment; + UINT8 relocatable_kernel; + UINT8 min_alignment; + UINT16 xloadflags; + UINT32 cmdline_size; + UINT32 hardware_subarch; + UINT64 hardware_subarch_data; + UINT32 payload_offset; + UINT32 payload_length; + UINT64 setup_data; + UINT64 pref_address; + UINT32 init_size; + UINT32 handover_offset; +} __attribute__((packed)); + +#ifdef __x86_64__ +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup); +static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { + handover_f handover; + + asm volatile ("cli"); + handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset); + handover(image, ST, setup); +} +#else +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0))); +static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { + handover_f handover; + + handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset); + handover(image, ST, setup); +} +#endif + +EFI_STATUS linux_exec(EFI_HANDLE *image, + CHAR8 *cmdline, UINTN cmdline_len, + UINTN linux_addr, + UINTN initrd_addr, UINTN initrd_size) { + struct SetupHeader *image_setup; + struct SetupHeader *boot_setup; + EFI_PHYSICAL_ADDRESS addr; + EFI_STATUS err; + + image_setup = (struct SetupHeader *)(linux_addr); + if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) + return EFI_LOAD_ERROR; + + if (image_setup->version < 0x20b || !image_setup->relocatable_kernel) + return EFI_LOAD_ERROR; + + addr = 0x3fffffff; + err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(0x4000), &addr); + if (EFI_ERROR(err)) + return err; + boot_setup = (struct SetupHeader *)(UINTN)addr; + ZeroMem(boot_setup, 0x4000); + CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader)); + boot_setup->loader_id = 0xff; + + boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512; + + if (cmdline) { + addr = 0xA0000; + err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); + if (EFI_ERROR(err)) + return err; + CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len); + ((CHAR8 *)addr)[cmdline_len] = 0; + boot_setup->cmd_line_ptr = (UINT32)addr; + } + + boot_setup->ramdisk_start = (UINT32)initrd_addr; + boot_setup->ramdisk_len = (UINT32)initrd_size; + + linux_efi_handover(image, boot_setup); + return EFI_LOAD_ERROR; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/linux.h b/alpine/packages/gummiboot/gummiboot/src/efi/linux.h new file mode 100644 index 000000000..e5d4f5a4f --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/linux.h @@ -0,0 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#ifndef __GUMMIBOOT_kernel_H +#define __GUMMIBOOT_kernel_H + +EFI_STATUS linux_exec(EFI_HANDLE *image, + CHAR8 *cmdline, UINTN cmdline_size, + UINTN linux_addr, + UINTN initrd_addr, UINTN initrd_size); +#endif diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/pefile.c b/alpine/packages/gummiboot/gummiboot/src/efi/pefile.c new file mode 100644 index 000000000..e6fedbc92 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/pefile.c @@ -0,0 +1,172 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "util.h" +#include "pefile.h" + +struct DosFileHeader { + UINT8 Magic[2]; + UINT16 LastSize; + UINT16 nBlocks; + UINT16 nReloc; + UINT16 HdrSize; + UINT16 MinAlloc; + UINT16 MaxAlloc; + UINT16 ss; + UINT16 sp; + UINT16 Checksum; + UINT16 ip; + UINT16 cs; + UINT16 RelocPos; + UINT16 nOverlay; + UINT16 reserved[4]; + UINT16 OEMId; + UINT16 OEMInfo; + UINT16 reserved2[10]; + UINT32 ExeHeader; +} __attribute__((packed)); + +#define PE_HEADER_MACHINE_I386 0x014c +#define PE_HEADER_MACHINE_X64 0x8664 +struct PeFileHeader { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; +} __attribute__((packed)); + +struct PeSectionHeader { + UINT8 Name[8]; + UINT32 VirtualSize; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} __attribute__((packed)); + + +EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { + EFI_FILE_HANDLE handle; + struct DosFileHeader dos; + uint8_t magic[4]; + struct PeFileHeader pe; + UINTN len; + UINTN i; + EFI_STATUS err; + + err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); + if (EFI_ERROR(err)) + return err; + + /* MS-DOS stub */ + len = sizeof(dos); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(dos)) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (CompareMem(dos.Magic, "MZ", 2) != 0) { + err = EFI_LOAD_ERROR; + goto out; + } + + err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader); + if (EFI_ERROR(err)) + goto out; + + /* PE header */ + len = sizeof(magic); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(magic)) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (CompareMem(magic, "PE\0\0", 2) != 0) { + err = EFI_LOAD_ERROR; + goto out; + } + + len = sizeof(pe); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(pe)) { + err = EFI_LOAD_ERROR; + goto out; + } + + /* PE32+ Subsystem type */ + if (pe.Machine != PE_HEADER_MACHINE_X64 && + pe.Machine != PE_HEADER_MACHINE_I386) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (pe.NumberOfSections > 96) { + err = EFI_LOAD_ERROR; + goto out; + } + + /* the sections start directly after the headers */ + err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader); + if (EFI_ERROR(err)) + goto out; + + for (i = 0; i < pe.NumberOfSections; i++) { + struct PeSectionHeader sect; + UINTN j; + + len = sizeof(sect); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, §); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(sect)) { + err = EFI_LOAD_ERROR; + goto out; + } + for (j = 0; sections[j]; j++) { + if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0) + continue; + + if (addrs) + addrs[j] = (UINTN)sect.VirtualAddress; + if (offsets) + offsets[j] = (UINTN)sect.PointerToRawData; + if (sizes) + sizes[j] = (UINTN)sect.VirtualSize; + } + } + +out: + uefi_call_wrapper(handle->Close, 1, handle); + return err; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/pefile.h b/alpine/packages/gummiboot/gummiboot/src/efi/pefile.h new file mode 100644 index 000000000..3adf1b07e --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/pefile.h @@ -0,0 +1,22 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#ifndef __GUMMIBOOT_PEFILE_H +#define __GUMMIBOOT_PEFILE_H + +EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, + CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); +#endif diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/stub.c b/alpine/packages/gummiboot/gummiboot/src/efi/stub.c new file mode 100644 index 000000000..e18faac66 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/stub.c @@ -0,0 +1,106 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "util.h" +#include "pefile.h" +#include "linux.h" + +/* magic string to find in the binary image */ +static const char __attribute__((used)) magic[] = "#### LoaderInfo: stub " VERSION " ####"; + +static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { + EFI_LOADED_IMAGE *loaded_image; + EFI_FILE *root_dir; + CHAR16 *loaded_image_path; + CHAR8 *b; + UINTN size; + BOOLEAN secure = FALSE; + CHAR8 *sections[] = { + (UINT8 *)".cmdline", + (UINT8 *)".linux", + (UINT8 *)".initrd", + NULL + }; + UINTN addrs[ELEMENTSOF(sections)-1] = {}; + UINTN offs[ELEMENTSOF(sections)-1] = {}; + UINTN szs[ELEMENTSOF(sections)-1] = {}; + CHAR8 *cmdline = NULL; + UINTN cmdline_len; + EFI_STATUS err; + + InitializeLib(image, sys_table); + + err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(err)) { + Print(L"Error getting a LoadedImageProtocol handle: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + + root_dir = LibOpenRoot(loaded_image->DeviceHandle); + if (!root_dir) { + Print(L"Unable to open root directory: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return EFI_LOAD_ERROR; + } + + loaded_image_path = DevicePathToStr(loaded_image->FilePath); + + if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { + if (*b > 0) + secure = TRUE; + FreePool(b); + } + + err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs); + if (EFI_ERROR(err)) { + Print(L"Unable to locate embedded .linux section: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + + if (szs[0] > 0) + cmdline = (CHAR8 *)(loaded_image->ImageBase + addrs[0]); + + cmdline_len = szs[0]; + + /* if we are not in secure boot mode, accept a custom command line and replace the built-in one */ + if (!secure && loaded_image->LoadOptionsSize > 0) { + CHAR16 *options; + CHAR8 *line; + UINTN i; + + options = (CHAR16 *)loaded_image->LoadOptions; + cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8); + line = AllocatePool(cmdline_len); + for (i = 0; i < cmdline_len; i++) + line[i] = options[i]; + cmdline = line; + } + + err = linux_exec(image, cmdline, cmdline_len, + (UINTN)loaded_image->ImageBase + addrs[1], + (UINTN)loaded_image->ImageBase + addrs[2], szs[2]); + + Print(L"Execution of embedded linux image failed: %r\n", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/util.c b/alpine/packages/gummiboot/gummiboot/src/efi/util.c new file mode 100644 index 000000000..ba5ed7d22 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/util.c @@ -0,0 +1,342 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#include +#include + +#include "util.h" + +/* + * Allocated random UUID, intended to be shared across tools that implement + * the (ESP)\loader\entries\-.conf convention and the + * associated EFI variables. + */ +static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; + +#ifdef __x86_64__ +UINT64 ticks_read(VOID) { + UINT64 a, d; + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d << 32) | a; +} +#else +UINT64 ticks_read(VOID) { + UINT64 val; + __asm__ volatile ("rdtsc" : "=A" (val)); + return val; +} +#endif + +/* count TSC ticks during a millisecond delay */ +UINT64 ticks_freq(VOID) { + UINT64 ticks_start, ticks_end; + + ticks_start = ticks_read(); + uefi_call_wrapper(BS->Stall, 1, 1000); + ticks_end = ticks_read(); + + return (ticks_end - ticks_start) * 1000; +} + +UINT64 time_usec(VOID) { + UINT64 ticks; + static UINT64 freq; + + ticks = ticks_read(); + if (ticks == 0) + return 0; + + if (freq == 0) { + freq = ticks_freq(); + if (freq == 0) + return 0; + } + + return 1000 * 1000 * ticks / freq; +} + +EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) { + if (strcmpa(v, (CHAR8 *)"1") == 0 || + strcmpa(v, (CHAR8 *)"yes") == 0 || + strcmpa(v, (CHAR8 *)"y") == 0 || + strcmpa(v, (CHAR8 *)"true") == 0) { + *b = TRUE; + return EFI_SUCCESS; + } + + if (strcmpa(v, (CHAR8 *)"0") == 0 || + strcmpa(v, (CHAR8 *)"no") == 0 || + strcmpa(v, (CHAR8 *)"n") == 0 || + strcmpa(v, (CHAR8 *)"false") == 0) { + *b = FALSE; + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { + UINT32 flags; + + flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + if (persistent) + flags |= EFI_VARIABLE_NON_VOLATILE; + + return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf); +} + +EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) { + return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent); +} + +EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) { + CHAR16 str[32]; + + SPrint(str, 32, L"%d", i); + return efivar_set(name, str, persistent); +} + +EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) { + CHAR8 *buf; + CHAR16 *val; + UINTN size; + EFI_STATUS err; + + err = efivar_get_raw(&loader_guid, name, &buf, &size); + if (EFI_ERROR(err)) + return err; + + val = StrDuplicate((CHAR16 *)buf); + if (!val) { + FreePool(buf); + return EFI_OUT_OF_RESOURCES; + } + + *value = val; + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) { + CHAR16 *val; + EFI_STATUS err; + + err = efivar_get(name, &val); + if (!EFI_ERROR(err)) { + *i = Atoi(val); + FreePool(val); + } + return err; +} + +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { + CHAR8 *buf; + UINTN l; + EFI_STATUS err; + + l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; + buf = AllocatePool(l); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf); + if (!EFI_ERROR(err)) { + *buffer = buf; + if (size) + *size = l; + } else + FreePool(buf); + return err; + +} + +VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) { + CHAR16 str[32]; + + if (usec == 0) + usec = time_usec(); + if (usec == 0) + return; + + SPrint(str, 32, L"%ld", usec); + efivar_set(name, str, FALSE); +} + +static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) { + CHAR16 unichar; + UINTN len; + UINTN i; + + if (stra[0] < 0x80) + len = 1; + else if ((stra[0] & 0xe0) == 0xc0) + len = 2; + else if ((stra[0] & 0xf0) == 0xe0) + len = 3; + else if ((stra[0] & 0xf8) == 0xf0) + len = 4; + else if ((stra[0] & 0xfc) == 0xf8) + len = 5; + else if ((stra[0] & 0xfe) == 0xfc) + len = 6; + else + return -1; + + switch (len) { + case 1: + unichar = stra[0]; + break; + case 2: + unichar = stra[0] & 0x1f; + break; + case 3: + unichar = stra[0] & 0x0f; + break; + case 4: + unichar = stra[0] & 0x07; + break; + case 5: + unichar = stra[0] & 0x03; + break; + case 6: + unichar = stra[0] & 0x01; + break; + } + + for (i = 1; i < len; i++) { + if ((stra[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= stra[i] & 0x3f; + } + + *c = unichar; + return len; +} + +CHAR16 *stra_to_str(CHAR8 *stra) { + UINTN strlen; + UINTN len; + UINTN i; + CHAR16 *str; + + len = strlena(stra); + str = AllocatePool((len + 1) * sizeof(CHAR16)); + + strlen = 0; + i = 0; + while (i < len) { + INTN utf8len; + + utf8len = utf8_to_16(stra + i, str + strlen); + if (utf8len <= 0) { + /* invalid utf8 sequence, skip the garbage */ + i++; + continue; + } + + strlen++; + i += utf8len; + } + str[strlen] = '\0'; + return str; +} + +CHAR16 *stra_to_path(CHAR8 *stra) { + CHAR16 *str; + UINTN strlen; + UINTN len; + UINTN i; + + len = strlena(stra); + str = AllocatePool((len + 2) * sizeof(CHAR16)); + + str[0] = '\\'; + strlen = 1; + i = 0; + while (i < len) { + INTN utf8len; + + utf8len = utf8_to_16(stra + i, str + strlen); + if (utf8len <= 0) { + /* invalid utf8 sequence, skip the garbage */ + i++; + continue; + } + + if (str[strlen] == '/') + str[strlen] = '\\'; + if (str[strlen] == '\\' && str[strlen-1] == '\\') { + /* skip double slashes */ + i += utf8len; + continue; + } + + strlen++; + i += utf8len; + } + str[strlen] = '\0'; + return str; +} + +CHAR8 *strchra(CHAR8 *s, CHAR8 c) { + do { + if (*s == c) + return s; + } while (*s++); + return NULL; +} + +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) { + EFI_FILE_HANDLE handle; + CHAR8 *buf; + UINTN buflen; + EFI_STATUS err; + UINTN len; + + err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL); + if (EFI_ERROR(err)) + return err; + + if (size == 0) { + EFI_FILE_INFO *info; + + info = LibFileInfo(handle); + buflen = info->FileSize+1; + FreePool(info); + } else + buflen = size; + + if (off > 0) { + err = uefi_call_wrapper(handle->SetPosition, 2, handle, off); + if (EFI_ERROR(err)) + return err; + } + + buf = AllocatePool(buflen); + err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf); + if (!EFI_ERROR(err)) { + buf[buflen] = '\0'; + *content = buf; + len = buflen; + } else { + len = err; + FreePool(buf); + } + + uefi_call_wrapper(handle->Close, 1, handle); + return len; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/efi/util.h b/alpine/packages/gummiboot/gummiboot/src/efi/util.h new file mode 100644 index 000000000..b86102e8f --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/efi/util.h @@ -0,0 +1,50 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#ifndef __GUMMIBOOT_UTIL_H +#define __GUMMIBOOT_UTIL_H + +#include +#include + +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +static inline const CHAR16 *yes_no(BOOLEAN b) { + return b ? L"yes" : L"no"; +} + +EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b); + +UINT64 ticks_read(void); +UINT64 ticks_freq(void); +UINT64 time_usec(void); + +EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent); +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent); +EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent); +VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec); + +EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value); +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size); +EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i); + +CHAR8 *strchra(CHAR8 *s, CHAR8 c); +CHAR16 *stra_to_path(CHAR8 *stra); +CHAR16 *stra_to_str(CHAR8 *stra); + +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content); +#endif diff --git a/alpine/packages/gummiboot/gummiboot/src/setup/efivars.c b/alpine/packages/gummiboot/gummiboot/src/setup/efivars.c new file mode 100644 index 000000000..7123257d6 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/setup/efivars.c @@ -0,0 +1,647 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efivars.h" + +bool is_efi_boot(void) { + return access("/sys/firmware/efi", F_OK) >= 0; +} + +static int read_flag(const char *varname) { + int r; + void *v; + size_t s; + uint8_t b; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, &v, &s); + if (r < 0) + return r; + + if (s != 1) { + r = -EINVAL; + goto finish; + } + + b = *(uint8_t *)v; + r = b > 0; +finish: + free(v); + return r; +} + +int is_efi_secure_boot(void) { + return read_flag("SecureBoot"); +} + +int is_efi_secure_boot_setup_mode(void) { + return read_flag("SetupMode"); +} + +int efi_get_variable( + const uint8_t vendor[16], + const char *name, + void **value, + size_t *size) { + + int fd = -1; + char *p = NULL; + uint32_t a; + ssize_t n; + struct stat st; + void *b; + int r; + + assert(name); + assert(value); + assert(size); + + if (asprintf(&p, + "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + name, + vendor[0], vendor[1], vendor[2], vendor[3], vendor[4], vendor[5], vendor[6], vendor[7], + vendor[8], vendor[9], vendor[10], vendor[11], vendor[12], vendor[13], vendor[14], vendor[15]) < 0) + return -ENOMEM; + + fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + r = -errno; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + if (st.st_size < 4) { + r = -EIO; + goto finish; + } + if (st.st_size > 4*1024*1024 + 4) { + r = -E2BIG; + goto finish; + } + + n = read(fd, &a, sizeof(a)); + if (n < 0) { + r = errno; + goto finish; + } + if (n != sizeof(a)) { + r = -EIO; + goto finish; + } + + b = malloc(st.st_size - 4 + 2); + if (!b) { + r = -ENOMEM; + goto finish; + } + + n = read(fd, b, (size_t) st.st_size - 4); + if (n < 0) { + free(b); + r = errno; + goto finish; + } + if (n != (ssize_t) st.st_size - 4) { + free(b); + r = -EIO; + goto finish; + } + + /* Always NUL terminate (2 bytes, to protect UTF-16) */ + ((char*) b)[st.st_size - 4] = 0; + ((char*) b)[st.st_size - 4 + 1] = 0; + + *value = b; + *size = (size_t) st.st_size - 4; + r = 0; + +finish: + if (fd >= 0) + close(fd); + free(p); + return r; +} + +int efi_set_variable( + const uint8_t vendor[16], + const char *name, + const void *value, + size_t size) { + + struct var { + uint32_t attr; + char buf[]; + } __attribute__((packed)) *buf = NULL; + char *p = NULL; + int fd = -1; + int r; + + assert(vendor); + assert(name); + + if (asprintf(&p, + "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + name, + vendor[0], vendor[1], vendor[2], vendor[3], vendor[4], vendor[5], vendor[6], vendor[7], + vendor[8], vendor[9], vendor[10], vendor[11], vendor[12], vendor[13], vendor[14], vendor[15]) < 0) + return -ENOMEM; + + if (size == 0) { + r = unlink(p); + goto finish; + } + + fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644); + if (fd < 0) { + r = -errno; + goto finish; + } + + buf = malloc(sizeof(uint32_t) + size); + if (!buf) { + r = -errno; + goto finish; + } + + buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + memcpy(buf->buf, value, size); + + r = write(fd, buf, sizeof(uint32_t) + size); + if (r < 0) { + r = -errno; + goto finish; + } + + if ((size_t)r != sizeof(uint32_t) + size) { + r = -EIO; + goto finish; + } + +finish: + if (fd >= 0) + close(fd); + free(buf); + free(p); + return r; +} + +int efi_get_variable_string(const uint8_t vendor[16], const char *name, char **p) { + void *s = NULL; + size_t ss; + char *x; + int r; + + r = efi_get_variable(vendor, name, &s, &ss); + if (r < 0) + return r; + + x = utf16_to_utf8(s, ss); + free(s); + if (!x) + return -ENOMEM; + + *p = x; + return 0; +} + +static size_t utf16_size(const uint16_t *s) { + size_t l = 0; + + while (s[l] > 0) + l++; + + return (l+1) * sizeof(uint16_t); +} + +struct guid { + uint32_t u1; + uint16_t u2; + uint16_t u3; + uint8_t u4[8]; +} __attribute__((packed)); + +static void efi_guid_to_id128(const void *guid, uint8_t *bytes) { + const struct guid *uuid = guid; + + bytes[0] = (uuid->u1 >> 24) & 0xff; + bytes[1] = (uuid->u1 >> 16) & 0xff; + bytes[2] = (uuid->u1 >> 8) & 0xff; + bytes[3] = (uuid->u1) & 0xff; + bytes[4] = (uuid->u2 >> 8) & 0xff; + bytes[5] = (uuid->u2) & 0xff; + bytes[6] = (uuid->u3 >> 8) & 0xff; + bytes[7] = (uuid->u3) & 0xff; + memcpy(bytes+8, uuid->u4, sizeof(uuid->u4)); +} + +static void id128_to_efi_guid(const uint8_t *bytes, void *guid) { + struct guid *uuid = guid; + + uuid->u1 = bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]; + uuid->u2 = bytes[4] << 8 | bytes[5]; + uuid->u3 = bytes[6] << 8 | bytes[7]; + memcpy(uuid->u4, bytes+8, sizeof(uuid->u4)); +} + +char *tilt_backslashes(char *s) { + char *p; + + for (p = s; *p; p++) + if (*p == '\\') + *p = '/'; + + return s; +} + +uint16_t *tilt_slashes(uint16_t *s) { + uint16_t *p; + + for (p = s; *p; p++) + if (*p == '/') + *p = '\\'; + + return s; +} + +#define LOAD_OPTION_ACTIVE 0x00000001 +#define MEDIA_DEVICE_PATH 0x04 +#define MEDIA_HARDDRIVE_DP 0x01 +#define MEDIA_FILEPATH_DP 0x04 +#define SIGNATURE_TYPE_GUID 0x02 +#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff + +struct boot_option { + uint32_t attr; + uint16_t path_len; + uint16_t title[]; +} __attribute__((packed)); + +struct drive_path { + uint32_t part_nr; + uint64_t part_start; + uint64_t part_size; + char signature[16]; + uint8_t mbr_type; + uint8_t signature_type; +} __attribute__((packed)); + +struct device_path { + uint8_t type; + uint8_t sub_type; + uint16_t length; + union { + uint16_t path[0]; + struct drive_path drive; + }; +} __attribute__((packed)); + +int efi_get_boot_option(uint16_t id, char **title, uint8_t part_uuid[16], char **path, bool *active) { + char boot_id[9]; + uint8_t *buf = NULL; + size_t l; + struct boot_option *header; + size_t title_size; + char *s = NULL; + char *p = NULL; + uint8_t p_uuid[16] = ""; + int err; + + snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); + err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, (void **) &buf, &l); + if (err < 0) + return err; + if (l < sizeof(struct boot_option)) { + err = -ENOENT; + goto err; + } + + header = (struct boot_option *) buf; + title_size = utf16_size(header->title); + if (title_size > l - offsetof(struct boot_option, title)) { + err = -EINVAL; + goto err; + } + + s = utf16_to_utf8(header->title, title_size); + if (!s) { + err = -ENOMEM; + goto err; + } + + if (header->path_len > 0) { + uint8_t *dbuf; + size_t dnext; + + dbuf = buf + offsetof(struct boot_option, title) + title_size; + dnext = 0; + while (dnext < header->path_len) { + struct device_path *dpath; + + dpath = (struct device_path *)(dbuf + dnext); + if (dpath->length < 4) + break; + + /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ + if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) + break; + + dnext += dpath->length; + + if (dpath->type != MEDIA_DEVICE_PATH) + continue; + + if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { + /* GPT Partition Table */ + if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) + continue; + + if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) + continue; + + efi_guid_to_id128(dpath->drive.signature, p_uuid); + continue; + } + + if (dpath->sub_type == MEDIA_FILEPATH_DP) { + p = utf16_to_utf8(dpath->path, dpath->length-4); + tilt_backslashes(p); + continue; + } + } + } + + if (title) + *title = s; + else + free(s); + + if (part_uuid) + memcpy(part_uuid, p_uuid, 16); + + if (path) + *path = p; + else + free(p); + + if (active) + *active = !!header->attr & LOAD_OPTION_ACTIVE; + + free(buf); + return 0; +err: + free(s); + free(p); + free(buf); + return err; +} + +static void to_utf16(uint16_t *dest, const char *src) { + int i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + dest[i] = '\0'; +} + +int efi_add_boot_option(uint16_t id, const char *title, + uint32_t part, uint64_t pstart, uint64_t psize, + const uint8_t part_uuid[16], + const char *path) { + char boot_id[9]; + char *buf; + size_t size; + size_t title_len; + size_t path_len; + struct boot_option *option; + struct device_path *devicep; + int err; + + title_len = (strlen(title)+1) * 2; + path_len = (strlen(path)+1) * 2; + + buf = calloc(sizeof(struct boot_option) + title_len + + sizeof(struct drive_path) + + sizeof(struct device_path) + path_len, 1); + if (!buf) { + err = -ENOMEM; + goto finish; + } + + /* header */ + option = (struct boot_option *)buf; + option->attr = LOAD_OPTION_ACTIVE; + option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + + offsetof(struct device_path, path) + path_len + + offsetof(struct device_path, path); + to_utf16(option->title, title); + size = offsetof(struct boot_option, title) + title_len; + + /* partition info */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_HARDDRIVE_DP; + devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); + devicep->drive.part_nr = part; + devicep->drive.part_start = pstart; + devicep->drive.part_size = psize; + devicep->drive.signature_type = SIGNATURE_TYPE_GUID; + devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + id128_to_efi_guid(part_uuid, devicep->drive.signature); + size += devicep->length; + + /* path to loader */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_FILEPATH_DP; + devicep->length = offsetof(struct device_path, path) + path_len; + to_utf16(devicep->path, path); + tilt_slashes(devicep->path); + size += devicep->length; + + /* end of path */ + devicep = (struct device_path *)(buf + size); + devicep->type = END_DEVICE_PATH_TYPE; + devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; + devicep->length = offsetof(struct device_path, path); + size += devicep->length; + + snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); + err = efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size); + +finish: + free(buf); + return err; +} + +int efi_remove_boot_option(uint16_t id) { + char boot_id[9]; + + snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); + return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0); +} + +int efi_get_boot_order(uint16_t **order) { + void *buf; + size_t l; + int r; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", &buf, &l); + if (r < 0) + return r; + + if (l <= 0) { + free(buf); + return -ENOENT; + } + + if ((l % sizeof(uint16_t) > 0)) { + free(buf); + return -EINVAL; + } + + *order = buf; + return (int) (l / sizeof(uint16_t)); +} + +int efi_set_boot_order(uint16_t *order, size_t n) { + return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t)); +} + +static int boot_id_hex(const char s[4]) { + int i; + int id = 0; + + for (i = 0; i < 4; i++) + if (s[i] >= '0' && s[i] <= '9') + id |= (s[i] - '0') << (3 - i) * 4; + else if (s[i] >= 'A' && s[i] <= 'F') + id |= (s[i] - 'A' + 10) << (3 - i) * 4; + else + return -1; + + return id; +} + +static int cmp_uint16(const void *_a, const void *_b) { + const uint16_t *a = _a, *b = _b; + + return (int)*a - (int)*b; +} + +int efi_get_boot_options(uint16_t **options) { + DIR *dir; + struct dirent *de; + uint16_t *list = NULL; + int count = 0; + + assert(options); + + dir = opendir("/sys/firmware/efi/efivars/"); + if (!dir) + return -errno; + + while ((de = readdir(dir))) { + int id; + uint16_t *t; + + if (strncmp(de->d_name, "Boot", 4) != 0) + continue; + + if (strlen(de->d_name) != 45) + continue; + + if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0) + continue; + + id = boot_id_hex(de->d_name + 4); + if (id < 0) + continue; + + t = realloc(list, (count + 1) * sizeof(uint16_t)); + if (!t) { + free(list); + closedir(dir); + return -ENOMEM; + } + list = t; + + list[count++] = id; + } + + closedir(dir); + qsort(list, count, sizeof(uint16_t), cmp_uint16); + *options = list; + + return count; +} + +char *utf16_to_utf8(const void *s, size_t length) { + char *r; + const uint8_t *f; + uint8_t *t; + + r = malloc((length*3+1)/2 + 1); + if (!r) + return NULL; + + t = (uint8_t*) r; + + for (f = s; f < (const uint8_t*) s + length; f += 2) { + uint16_t c; + + c = (f[1] << 8) | f[0]; + + if (c == 0) { + *t = 0; + return r; + } else if (c < 0x80) { + *(t++) = (uint8_t) c; + } else if (c < 0x800) { + *(t++) = (uint8_t) (0xc0 | (c >> 6)); + *(t++) = (uint8_t) (0x80 | (c & 0x3f)); + } else { + *(t++) = (uint8_t) (0xe0 | (c >> 12)); + *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f)); + *(t++) = (uint8_t) (0x80 | (c & 0x3f)); + } + } + + *t = 0; + + return r; +} diff --git a/alpine/packages/gummiboot/gummiboot/src/setup/efivars.h b/alpine/packages/gummiboot/gummiboot/src/setup/efivars.h new file mode 100644 index 000000000..dfe7355d9 --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/setup/efivars.h @@ -0,0 +1,55 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + +#define EFI_VENDOR_GLOBAL ((uint8_t[16]) { 0x8b,0xe4,0xdf,0x61,0x93,0xca,0x11,0xd2,0xaa,0x0d,0x00,0xe0,0x98,0x03,0x2b,0x8c }) +#define EFI_VENDOR_LOADER ((uint8_t[16]) { 0x4a,0x67,0xb0,0x82,0x0a,0x4c,0x41,0xcf,0xb6,0xc7,0x44,0x0b,0x29,0xbb,0x8c,0x4f }) + +bool is_efi_boot(void); +int is_efi_secure_boot(void); +int is_efi_secure_boot_setup_mode(void); +int efi_get_variable(const uint8_t vendor[16], const char *name, void **value, size_t *size); +int efi_set_variable( const uint8_t vendor[16], const char *name, const void *value, size_t size); +int efi_get_variable_string(const uint8_t vendor[16], const char *name, char **p); +int efi_get_boot_option(uint16_t id, char **title, uint8_t part_uuid[16], char **path, bool *active); + +int efi_get_boot_options(uint16_t **options); +int efi_add_boot_option(uint16_t id, const char *title, + uint32_t part, uint64_t pstart, uint64_t psize, + const uint8_t part_uuid[16], + const char *path); +int efi_remove_boot_option(uint16_t id); + +int efi_get_boot_order(uint16_t **order); +int efi_set_boot_order(uint16_t *order, size_t n); + +char *utf16_to_utf8(const void *s, size_t length); +char *tilt_backslashes(char *s); +uint16_t *tilt_slashes(uint16_t *s); diff --git a/alpine/packages/gummiboot/gummiboot/src/setup/setup.c b/alpine/packages/gummiboot/gummiboot/src/setup/setup.c new file mode 100644 index 000000000..6a4275a2a --- /dev/null +++ b/alpine/packages/gummiboot/gummiboot/src/setup/setup.c @@ -0,0 +1,1425 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efivars.h" + +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) +#define streq(a,b) (strcmp((a),(b)) == 0) +#define UUID_EMPTY ((uint8_t[16]) {}) + +static inline bool streq_ptr(const char *a, const char *b) { + if (a && b) + return streq(a, b); + if (!a && !b) + return true; + return false; +} + +static inline bool isempty(const char *p) { + return !p || !p[0]; +} + +static inline const char *strna(const char *s) { + return isempty(s) ? "n/a" : s; +} + +static int uuid_parse(const char *s, uint8_t uuid[16]) { + int u[16]; + int i; + + if (sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &u[0], &u[1], &u[2], &u[3], &u[4], &u[5], &u[6], &u[7], + &u[8], &u[9], &u[10], &u[11], &u[12], &u[13], &u[14], &u[15]) != 16) + return -EINVAL; + + for (i = 0; i < 16; i++) + uuid[i] = u[i]; + + return 0; +} + +static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, uint8_t uuid[16]) { + struct statfs sfs; + struct stat st, st2; + char *t; + blkid_probe b = NULL; + int r; + const char *v; + + if (statfs(p, &sfs) < 0) { + fprintf(stderr, "Failed to check file system type of %s: %m\n", p); + return -errno; + } + + if (sfs.f_type != 0x4d44) { + fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system.\n", p); + return -ENODEV; + } + + if (stat(p, &st) < 0) { + fprintf(stderr, "Failed to determine block device node of %s: %m\n", p); + return -errno; + } + + if (major(st.st_dev) == 0) { + fprintf(stderr, "Block device node of %p is invalid.\n", p); + return -ENODEV; + } + + r = asprintf(&t, "%s/..", p); + if (r < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + r = stat(t, &st2); + free(t); + if (r < 0) { + fprintf(stderr, "Failed to determine block device node of parent of %s: %m\n", p); + return -errno; + } + + if (st.st_dev == st2.st_dev) { + fprintf(stderr, "Directory %s is not the root of the EFI System Partition (ESP) file system.\n", p); + return -ENODEV; + } + + r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); + if (r < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + errno = 0; + b = blkid_new_probe_from_filename(t); + free(t); + if (!b) { + if (errno != 0) { + fprintf(stderr, "Failed to open file system %s: %m\n", p); + return -errno; + } + + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) { + fprintf(stderr, "File system %s is ambigious.\n", p); + r = -ENODEV; + goto fail; + } else if (r == 1) { + fprintf(stderr, "File system %s does not contain a label.\n", p); + r = -ENODEV; + goto fail; + } else if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe file system %s: %s\n", p, strerror(-r)); + goto fail; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe file system type %s: %s\n", p, strerror(-r)); + goto fail; + } + + if (strcmp(v, "vfat") != 0) { + fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system after all.\n", p); + r = -ENODEV; + goto fail; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition scheme %s: %s\n", p, strerror(-r)); + goto fail; + } + + if (strcmp(v, "gpt") != 0) { + fprintf(stderr, "File system %s is not on a GPT partition table.\n", p); + r = -ENODEV; + goto fail; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition type UUID %s: %s\n", p, strerror(-r)); + goto fail; + } + + if (strcmp(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b") != 0) { + r = -ENODEV; + fprintf(stderr, "File system %s is not an EFI System Partition (ESP).\n", p); + goto fail; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition entry UUID %s: %s\n", p, strerror(-r)); + goto fail; + } + uuid_parse(v, uuid); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition number %s: %s\n", p, strerror(-r)); + goto fail; + } + *part = strtoul(v, NULL, 10); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition offset %s: %s\n", p, strerror(-r)); + goto fail; + } + *pstart = strtoul(v, NULL, 10); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + fprintf(stderr, "Failed to probe partition size %s: %s\n", p, strerror(-r)); + goto fail; + } + *psize = strtoul(v, NULL, 10); + + blkid_free_probe(b); + return 0; +fail: + if (b) + blkid_free_probe(b); + return r; +} + +/* search for "#### LoaderInfo: gummiboot 31 ####" string inside the binary */ +static int get_file_version(FILE *f, char **v) { + struct stat st; + char *buf; + const char *s, *e; + char *x = NULL; + int r = 0; + + assert(f); + assert(v); + + if (fstat(fileno(f), &st) < 0) + return -errno; + + if (st.st_size < 27) + return 0; + + buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0); + if (buf == MAP_FAILED) + return -errno; + + s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17); + if (!s) + goto finish; + s += 17; + + e = memmem(s, st.st_size - (s - buf), " ####", 5); + if (!e || e - s < 3) { + fprintf(stderr, "Malformed version string.\n"); + r = -EINVAL; + goto finish; + } + + x = strndup(s, e - s); + if (!x) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + r = 1; + +finish: + munmap(buf, st.st_size); + *v = x; + return r; +} + +static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { + struct dirent *de; + char *p = NULL, *q = NULL; + DIR *d = NULL; + int r = 0, c = 0; + + if (asprintf(&p, "%s/%s", esp_path, path) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + d = opendir(p); + if (!d) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + fprintf(stderr, "Failed to read %s: %m\n", p); + r = -errno; + goto finish; + } + + while ((de = readdir(d))) { + char *v; + size_t n; + FILE *f; + + if (de->d_name[0] == '.') + continue; + + n = strlen(de->d_name); + if (n < 4 || strcasecmp(de->d_name + n - 4, ".efi") != 0) + continue; + + if (prefix && strncasecmp(de->d_name, prefix, strlen(prefix)) != 0) + continue; + + free(q); + q = NULL; + if (asprintf(&q, "%s/%s/%s", esp_path, path, de->d_name) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + f = fopen(q, "re"); + if (!f) { + fprintf(stderr, "Failed to open %s for reading: %m\n", q); + r = -errno; + goto finish; + } + + r = get_file_version(f, &v); + fclose(f); + + if (r < 0) + goto finish; + + if (r > 0) + printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v); + else + printf(" File: └─/%s/%s\n", path, de->d_name); + + c++; + free(v); + } + + r = c; + +finish: + if (d) + closedir(d); + + free(p); + free(q); + return r; +} + +static int status_binaries(const char *esp_path, uint8_t partition[16]) { + int r; + + printf("Boot Loader Binaries:\n"); + + printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + partition[0], partition[1], partition[2], partition[3], partition[4], partition[5], partition[6], partition[7], + partition[8], partition[9], partition[10], partition[11], partition[12], partition[13], partition[14], partition[15]); + + r = enumerate_binaries(esp_path, "EFI/gummiboot", NULL); + if (r == 0) + fprintf(stderr, "Gummiboot not installed in ESP.\n"); + else if (r < 0) + return r; + + r = enumerate_binaries(esp_path, "EFI/Boot", "boot"); + if (r == 0) + fprintf(stderr, "No default/fallback boot loader installed in ESP.\n"); + else if (r < 0) + return r; + + printf("\n"); + return 0; +} + +static int print_efi_option(uint16_t id, bool in_order) { + char *title = NULL; + char *path = NULL; + uint8_t partition[16]; + bool active; + int r = 0; + + r = efi_get_boot_option(id, &title, partition, &path, &active); + if (r < 0) + goto finish; + + /* print only configured entries with partition information */ + if (!path || memcmp(partition, UUID_EMPTY, 16) == 0) + return 0; + + printf(" Title: %s\n", strna(title)); + printf(" ID: 0x%04X\n", id); + printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); + printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + partition[0], partition[1], partition[2], partition[3], partition[4], partition[5], partition[6], partition[7], + partition[8], partition[9], partition[10], partition[11], partition[12], partition[13], partition[14], partition[15]); + printf(" File: └─%s\n", path); + printf("\n"); + +finish: + free(title); + free(path); + return r; +} + +static int status_variables(void) { + int n_options, n_order; + uint16_t *options = NULL, *order = NULL; + int r, i; + + if (!is_efi_boot()) { + fprintf(stderr, "Not booted with EFI, not showing EFI variables.\n"); + return 0; + } + + n_options = efi_get_boot_options(&options); + if (n_options < 0) { + if (n_options == -ENOENT) + fprintf(stderr, "Failed to access EFI variables, " + "efivarfs needs to be available at /sys/firmware/efi/efivars/.\n"); + else + fprintf(stderr, "Failed to read EFI boot entries: %s\n", strerror(-n_options)); + r = n_options; + goto finish; + } + + printf("Boot Loader Entries in EFI Variables:\n"); + n_order = efi_get_boot_order(&order); + if (n_order == -ENOENT) { + n_order = 0; + } else if (n_order < 0) { + fprintf(stderr, "Failed to read EFI boot order.\n"); + r = n_order; + goto finish; + } + + /* print entries in BootOrder first */ + for (i = 0; i < n_order; i++) + print_efi_option(order[i], true); + + /* print remaining entries */ + for (i = 0; i < n_options; i++) { + int j; + bool found = false; + + for (j = 0; j < n_order; j++) + if (options[i] == order[j]) { + found = true; + break; + } + + if (found) + continue; + + print_efi_option(options[i], false); + } + + r = 0; +finish: + free(options); + free(order); + + return r; +} + +static int compare_product(const char *a, const char *b) { + size_t x, y; + + assert(a); + assert(b); + + x = strcspn(a, " "); + y = strcspn(b, " "); + if (x != y) + return x < y ? -1 : x > y ? 1 : 0; + + return strncmp(a, b, x); +} + +static int compare_version(const char *a, const char *b) { + assert(a); + assert(b); + + a += strcspn(a, " "); + a += strspn(a, " "); + b += strcspn(b, " "); + b += strspn(b, " "); + + return strverscmp(a, b); +} + +static int version_check(FILE *f, const char *from, const char *to) { + FILE *g = NULL; + char *a = NULL, *b = NULL; + int r; + + assert(f); + assert(from); + assert(to); + + r = get_file_version(f, &a); + if (r < 0) + goto finish; + if (r == 0) { + r = -EINVAL; + fprintf(stderr, "Source file %s does not carry version information!\n", from); + goto finish; + } + + g = fopen(to, "re"); + if (!g) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + r = -errno; + fprintf(stderr, "Failed to open %s for reading: %m\n", to); + goto finish; + } + + r = get_file_version(g, &b); + if (r < 0) + goto finish; + if (r == 0 || compare_product(a, b) != 0) { + r = -EEXIST; + fprintf(stderr, "Skipping %s, since it's owned by another boot loader.\n", to); + goto finish; + } + + if (compare_version(a, b) < 0) { + r = -EEXIST; + fprintf(stderr, "Skipping %s, since it's a newer boot loader version already.\n", to); + goto finish; + } + + r = 0; + +finish: + free(a); + free(b); + if (g) + fclose(g); + return r; +} + +static int copy_file(const char *from, const char *to, bool force) { + FILE *f = NULL, *g = NULL; + char *p = NULL; + int r; + struct timespec t[2]; + struct stat st; + + assert(from); + assert(to); + + f = fopen(from, "re"); + if (!f) { + fprintf(stderr, "Failed to open %s for reading: %m\n", from); + return -errno; + } + + if (!force) { + /* If this is an update, then let's compare versions first */ + r = version_check(f, from, to); + if (r < 0) + goto finish; + } + + if (asprintf(&p, "%s~", to) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + g = fopen(p, "wxe"); + if (!g) { + /* Directory doesn't exist yet? Then let's skip this... */ + if (!force && errno == ENOENT) { + r = 0; + goto finish; + } + + fprintf(stderr, "Failed to open %s for writing: %m\n", to); + r = -errno; + goto finish; + } + + rewind(f); + do { + size_t k; + uint8_t buf[32*1024]; + + k = fread(buf, 1, sizeof(buf), f); + if (ferror(f)) { + fprintf(stderr, "Failed to read %s: %m\n", from); + r = -errno; + goto finish; + } + if (k == 0) + break; + + fwrite(buf, 1, k, g); + if (ferror(g)) { + fprintf(stderr, "Failed to write %s: %m\n", to); + r = -errno; + goto finish; + } + } while (!feof(f)); + + fflush(g); + if (ferror(g)) { + fprintf(stderr, "Failed to write %s: %m\n", to); + r = -errno; + goto finish; + } + + r = fstat(fileno(f), &st); + if (r < 0) { + fprintf(stderr, "Failed to get file timestamps of %s: %m", from); + r = -errno; + goto finish; + } + + t[0] = st.st_atim; + t[1] = st.st_mtim; + + r = futimens(fileno(g), t); + if (r < 0) { + fprintf(stderr, "Failed to change file timestamps for %s: %m", p); + r = -errno; + goto finish; + } + + if (rename(p, to) < 0) { + fprintf(stderr, "Failed to rename %s to %s: %m\n", p, to); + r = -errno; + goto finish; + } + + fprintf(stderr, "Copied %s to %s.\n", from, to); + + free(p); + p = NULL; + r = 0; + +finish: + if (f) + fclose(f); + if (g) + fclose(g); + if (p) { + unlink(p); + free(p); + } + return r; +} + +static char* strupper(char *s) { + char *p; + + for (p = s; *p; p++) + *p = toupper(*p); + + return s; +} + +static int mkdir_one(const char *prefix, const char *suffix) { + char *p; + + if (asprintf(&p, "%s/%s", prefix, suffix) < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + if (mkdir(p, 0700) < 0) { + if (errno != EEXIST) { + fprintf(stderr, "Failed to create %s: %m\n", p); + free(p); + return -errno; + } + } else + fprintf(stderr, "Created %s.\n", p); + + free(p); + return 0; +} + +static int create_dirs(const char *esp_path) { + int r; + + r = mkdir_one(esp_path, "EFI"); + if (r < 0) + return r; + + r = mkdir_one(esp_path, "EFI/gummiboot"); + if (r < 0) + return r; + + r = mkdir_one(esp_path, "EFI/Boot"); + if (r < 0) + return r; + + r = mkdir_one(esp_path, "loader"); + if (r < 0) + return r; + + r = mkdir_one(esp_path, "loader/entries"); + if (r < 0) + return r; + + return 0; +} + +static int copy_one_file(const char *esp_path, const char *name, bool force) { + char *p = NULL, *q = NULL, *v = NULL; + int r; + + if (asprintf(&p, GUMMIBOOTLIBDIR "/%s", name) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + if (asprintf(&q, "%s/EFI/gummiboot/%s", esp_path, name) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + r = copy_file(p, q, force); + + if (strncmp(name, "gummiboot", 9) == 0) { + int k; + + /* Create the EFI default boot loader name (specified for removable devices) */ + if (asprintf(&v, "%s/EFI/Boot/%s", esp_path, name + 5) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + strupper(strrchr(v, '/') + 1); + + k = copy_file(p, v, force); + if (k < 0 && r == 0) { + r = k; + goto finish; + } + } + +finish: + free(p); + free(q); + free(v); + return r; +} + +static int install_binaries(const char *esp_path, bool force) { + struct dirent *de; + DIR *d; + int r = 0; + + if (force) { + /* Don't create any of these directories when we are + * just updating. When we update we'll drop-in our + * files (unless there are newer ones already), but we + * won't create the directories for them in the first + * place. */ + r = create_dirs(esp_path); + if (r < 0) + return r; + } + + d = opendir(GUMMIBOOTLIBDIR); + if (!d) { + fprintf(stderr, "Failed to open "GUMMIBOOTLIBDIR": %m\n"); + return -errno; + } + + while ((de = readdir(d))) { + size_t n; + int k; + + if (de->d_name[0] == '.') + continue; + + n = strlen(de->d_name); + if (n < 4 || strcmp(de->d_name + n - 4, ".efi") != 0) + continue; + + k = copy_one_file(esp_path, de->d_name, force); + if (k < 0 && r == 0) + r = k; + } + + closedir(d); + return r; +} + +static bool same_entry(uint16_t id, const uint8_t uuid[16], const char *path) { + char *opath = NULL; + uint8_t ouuid[16]; + int err; + bool same = false; + + err = efi_get_boot_option(id, NULL, ouuid, &opath, NULL); + if (err < 0) + return false; + if (memcmp(uuid, ouuid, 16) != 0) + goto finish; + + if (!streq_ptr(path, opath)) + goto finish; + + same = true; + +finish: + free(opath); + return same; +} + +static int find_slot(const uint8_t uuid[16], const char *path, uint16_t *id) { + uint16_t *options = NULL; + int n_options; + int i; + uint16_t new_id = 0; + bool existing = false; + + n_options = efi_get_boot_options(&options); + if (n_options < 0) + return n_options; + + /* find already existing gummiboot entry */ + for (i = 0; i < n_options; i++) + if (same_entry(options[i], uuid, path)) { + new_id = options[i]; + existing = true; + goto finish; + } + + /* find free slot in the sorted BootXXXX variable list */ + for (i = 0; i < n_options; i++) + if (i != options[i]) { + new_id = i; + goto finish; + } + + /* use the next one */ + if (i == 0xffff) + return -ENOSPC; + new_id = i; + +finish: + *id = new_id; + free(options); + return existing; +} + +static int insert_into_order(uint16_t slot, bool first) { + uint16_t *order = NULL; + uint16_t *new_order; + int n_order; + int i; + int err = 0; + + n_order = efi_get_boot_order(&order); + if (n_order <= 0) { + /* no entry, add us */ + err = efi_set_boot_order(&slot, 1); + goto finish; + } + + /* are we the first and only one? */ + if (n_order == 1 && order[0] == slot) + goto finish; + + /* are we already in the boot order? */ + for (i = 0; i < n_order; i++) { + if (order[i] != slot) + continue; + + /* we do not require to be the first one, all is fine */ + if (!first) + goto finish; + + /* move us to the first slot */ + memmove(&order[1], order, i * sizeof(uint16_t)); + order[0] = slot; + efi_set_boot_order(order, n_order); + goto finish; + } + + /* extend array */ + new_order = realloc(order, (n_order+1) * sizeof(uint16_t)); + if (!new_order) { + err = -ENOMEM; + goto finish; + } + order = new_order; + + /* add us to the top or end of the list */ + if (first) { + memmove(&order[1], order, n_order * sizeof(uint16_t)); + order[0] = slot; + } else + order[n_order] = slot; + + efi_set_boot_order(order, n_order+1); + +finish: + free(order); + return err; +} + +static int remove_from_order(uint16_t slot) { + uint16_t *order = NULL; + int n_order; + int i; + int err = 0; + + n_order = efi_get_boot_order(&order); + if (n_order < 0) + return n_order; + if (n_order == 0) + return 0; + + for (i = 0; i < n_order; i++) { + if (order[i] != slot) + continue; + + if (i+1 < n_order) + memmove(&order[i], &order[i+1], (n_order - i) * sizeof(uint16_t)); + efi_set_boot_order(order, n_order-1); + break; + } + + free(order); + return err; +} + +static int install_variables(const char *esp_path, + uint32_t part, uint64_t pstart, uint64_t psize, + const uint8_t uuid[16], const char *path, + bool first) { + char *p = NULL; + uint16_t *options = NULL; + uint16_t slot; + int r; + + if (!is_efi_boot()) { + fprintf(stderr, "Not booted with EFI, skipping EFI variable setup.\n"); + return 0; + } + + if (asprintf(&p, "%s%s", esp_path, path) < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + if (access(p, F_OK) < 0) { + if (errno == ENOENT) + r = 0; + else + r = -errno; + goto finish; + } + + r = find_slot(uuid, path, &slot); + if (r < 0) { + if (r == -ENOENT) + fprintf(stderr, "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?\n"); + else + fprintf(stderr, "Failed to determine current boot order: %s\n", strerror(-r)); + goto finish; + } + + if (first || r == false) { + r = efi_add_boot_option(slot, "Linux Boot Manager", + part, pstart, psize, + uuid, path); + if (r < 0) { + fprintf(stderr, "Failed to create EFI Boot variable entry: %s\n", strerror(-r)); + goto finish; + } + fprintf(stderr, "Created EFI boot entry \"Linux Boot Manager\".\n"); + } + + insert_into_order(slot, first); + +finish: + free(p); + free(options); + return r; +} + +static int delete_nftw(const char *path, const struct stat *sb, int typeflag, struct FTW *ftw) { + int r; + + if (typeflag == FTW_D || typeflag == FTW_DNR || typeflag == FTW_DP) + r = rmdir(path); + else + r = unlink(path); + + if (r < 0) + fprintf(stderr, "Failed to remove %s: %m\n", path); + else + fprintf(stderr, "Removed %s.\n", path); + + return 0; +} + +static int rm_rf(const char *p) { + nftw(p, delete_nftw, 20, FTW_DEPTH|FTW_MOUNT|FTW_PHYS); + return 0; +} + +static int remove_boot_efi(const char *esp_path) { + struct dirent *de; + char *p = NULL, *q = NULL; + DIR *d = NULL; + int r = 0, c = 0; + + if (asprintf(&p, "%s/EFI/Boot", esp_path) < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + d = opendir(p); + if (!d) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + fprintf(stderr, "Failed to read %s: %m\n", p); + r = -errno; + goto finish; + } + + while ((de = readdir(d))) { + char *v; + size_t n; + FILE *f; + + if (de->d_name[0] == '.') + continue; + + n = strlen(de->d_name); + if (n < 4 || strcasecmp(de->d_name + n - 4, ".EFI") != 0) + continue; + + if (strncasecmp(de->d_name, "Boot", 4) != 0) + continue; + + free(q); + q = NULL; + if (asprintf(&q, "%s/%s", p, de->d_name) < 0) { + fprintf(stderr, "Out of memory.\n"); + r = -ENOMEM; + goto finish; + } + + f = fopen(q, "re"); + if (!f) { + fprintf(stderr, "Failed to open %s for reading: %m\n", q); + r = -errno; + goto finish; + } + + r = get_file_version(f, &v); + fclose(f); + + if (r < 0) + goto finish; + + if (r > 0 && strncmp(v, "gummiboot ", 10) == 0) { + + r = unlink(q); + if (r < 0) { + fprintf(stderr, "Failed to remove %s: %m\n", q); + r = -errno; + free(v); + goto finish; + } else + fprintf(stderr, "Removed %s.\n", q); + } + + c++; + free(v); + } + + r = c; + +finish: + if (d) + closedir(d); + free(p); + free(q); + + return r; +} + +static int rmdir_one(const char *prefix, const char *suffix) { + char *p; + + if (asprintf(&p, "%s/%s", prefix, suffix) < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + if (rmdir(p) < 0) { + if (errno != ENOENT && errno != ENOTEMPTY) { + fprintf(stderr, "Failed to remove %s: %m\n", p); + free(p); + return -errno; + } + } else + fprintf(stderr, "Removed %s.\n", p); + + free(p); + return 0; +} + + +static int remove_binaries(const char *esp_path) { + char *p; + int r, q; + + if (asprintf(&p, "%s/EFI/gummiboot", esp_path) < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + r = rm_rf(p); + free(p); + + q = remove_boot_efi(esp_path); + if (q < 0 && r == 0) + r = q; + + q = rmdir_one(esp_path, "loader/entries"); + if (q < 0 && r == 0) + r = q; + + q = rmdir_one(esp_path, "loader"); + if (q < 0 && r == 0) + r = q; + + q = rmdir_one(esp_path, "EFI/Boot"); + if (q < 0 && r == 0) + r = q; + + q = rmdir_one(esp_path, "EFI/gummiboot"); + if (q < 0 && r == 0) + r = q; + + q = rmdir_one(esp_path, "EFI"); + if (q < 0 && r == 0) + r = q; + + return r; +} + +static int remove_variables(const uint8_t uuid[16], const char *path, bool in_order) { + uint16_t slot; + int r; + + if (!is_efi_boot()) + return 0; + + r = find_slot(uuid, path, &slot); + if (r != 1) + return 0; + + r = efi_remove_boot_option(slot); + if (r < 0) + return r; + + if (in_order) + remove_from_order(slot); + + return 0; +} + +static int install_loader_config(const char *esp_path) { + char *p = NULL; + char line[64]; + char *machine = NULL; + FILE *f; + + f = fopen("/etc/machine-id", "re"); + if (!f) + return -errno; + + if (fgets(line, sizeof(line), f) != NULL) { + char *s; + + s = strchr(line, '\n'); + if (s) + s[0] = '\0'; + if (strlen(line) == 32) + machine = line; + } + + fclose(f); + + if (!machine) + return -ESRCH; + + if (asprintf(&p, "%s/%s", esp_path, "loader/loader.conf") < 0) { + fprintf(stderr, "Out of memory.\n"); + return -ENOMEM; + } + + f = fopen(p, "wxe"); + if (f) { + fprintf(f, "#timeout 3\n"); + fprintf(f, "default %s-*\n", machine); + fclose(f); + } + + free(p); + return 0; +} + +static int help(void) { + printf("%s [COMMAND] [OPTIONS...]\n" + "\n" + "Install, update or remove the Gummiboot EFI boot loader.\n\n" + " -h --help Show this help\n" + " --version Print version\n" + " --path=PATH Path to the EFI System Partition (ESP)\n" + " --no-variables Don't touch EFI variables\n" + "\n" + "Comands:\n" + " status Show status of installed Gummiboot and EFI variables\n" + " install Install Gummiboot to the ESP and EFI variables\n" + " update Update Gummiboot in the ESP and EFI variables\n" + " remove Remove Gummiboot from the ESP and EFI variables\n", + program_invocation_short_name); + + return 0; +} + +static const char *arg_path = NULL; +static bool arg_touch_variables = true; + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_PATH = 0x100, + ARG_VERSION, + ARG_NO_VARIABLES, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "path", required_argument, NULL, ARG_PATH }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + printf(VERSION "\n"); + return 0; + + case ARG_PATH: + arg_path = optarg; + break; + + case ARG_NO_VARIABLES: + arg_touch_variables = false; + break; + + case '?': + return -EINVAL; + + default: + fprintf(stderr, "Unknown option code '%c'.\n", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char*argv[]) { + enum action { + ACTION_STATUS, + ACTION_INSTALL, + ACTION_UPDATE, + ACTION_REMOVE + } arg_action = ACTION_STATUS; + + static const struct { + const char* verb; + enum action action; + } verbs[] = { + { "status", ACTION_STATUS }, + { "install", ACTION_INSTALL }, + { "update", ACTION_UPDATE }, + { "remove", ACTION_REMOVE }, + }; + + uint8_t uuid[16] = ""; + uint32_t part = 0; + uint64_t pstart = 0; + uint64_t psize = 0; + unsigned int i; + int q; + int r; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (argv[optind]) { + for (i = 0; i < ELEMENTSOF(verbs); i++) { + if (!streq(argv[optind], verbs[i].verb)) + continue; + arg_action = verbs[i].action; + break; + } + if (i >= ELEMENTSOF(verbs)) { + fprintf(stderr, "Unknown operation %s\n", argv[optind]); + r = -EINVAL; + goto finish; + } + } + + if (!arg_path) + arg_path = "/boot"; + + if (geteuid() != 0) { + fprintf(stderr, "Need to be root.\n"); + r = -EPERM; + goto finish; + } + + r = verify_esp(arg_path, &part, &pstart, &psize, uuid); + if (r == -ENODEV && !arg_path) + fprintf(stderr, "You might want to use --path= to indicate the path to your ESP, in case it is not mounted to /boot.\n"); + if (r < 0) + goto finish; + + switch (arg_action) { + case ACTION_STATUS: + r = status_binaries(arg_path, uuid); + if (r < 0) + goto finish; + + if (arg_touch_variables) + r = status_variables(); + break; + + case ACTION_INSTALL: + case ACTION_UPDATE: + umask(0002); + + r = install_binaries(arg_path, arg_action == ACTION_INSTALL); + if (r < 0) + goto finish; + + if (arg_action == ACTION_INSTALL) + install_loader_config(arg_path); + + if (arg_touch_variables) + r = install_variables(arg_path, + part, pstart, psize, uuid, + "/EFI/gummiboot/gummiboot" MACHINE_TYPE_NAME ".efi", + arg_action == ACTION_INSTALL); + break; + + case ACTION_REMOVE: + r = remove_binaries(arg_path); + + if (arg_touch_variables) { + q = remove_variables(uuid, "/EFI/gummiboot/gummiboot" MACHINE_TYPE_NAME ".efi", true); + if (q < 0 && r == 0) + r = q; + } + break; + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +}