├── .github └── workflows │ └── ci.yml ├── .gitignore ├── AUTHORS ├── CODE_OF_CONDUCT.md ├── COPYING ├── INSTALL ├── MODULE_LICENSE_GPL2 ├── Make.coverity ├── Make.defaults ├── Make.deps ├── Make.fanalyzer ├── Make.rules ├── Make.scan-build ├── Make.version ├── Makefile ├── NOTICE ├── README ├── README.md ├── TODO ├── efibootmgr.spec.in └── src ├── .gitignore ├── Android.mk ├── Makefile ├── efi.c ├── efi.h ├── efibootdump.8.in ├── efibootdump.c ├── efibootmgr.8.in ├── efibootmgr.c ├── efibootmgr.h ├── efibootnext.c ├── eficonman.c ├── error.h ├── fix_coverity.h ├── list.h ├── parse_loader_data.c └── parse_loader_data.h /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pull-request", 3 | "on": { 4 | "push": { "branches": "main" }, 5 | "pull_request": { "branches": "main" }, 6 | }, 7 | "jobs": { 8 | "linux": { 9 | "runs-on": "ubuntu-latest", 10 | "container": "vathpela/efi-ci:f36-x64", 11 | "steps": [ 12 | { "uses": "actions/checkout@v2" }, 13 | { "run": "EFIDIR=test make" }, 14 | ], 15 | }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | *.E 3 | *.o 4 | *.patch 5 | *.S 6 | efibootmgr-*.tar.* 7 | core.* 8 | efibootmgr*.zip 9 | efibootmgr.spec 10 | .*.d 11 | cov-int 12 | scan-results/ 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Matt Domsch 2 | - All .c and .h files 3 | 4 | Andreas Schwab 5 | - Patches to several .c and .h files 6 | 7 | Richard Hirst 8 | - Patch to efichar.c 9 | 10 | dann frazier 11 | - docbook of manpage 12 | - Patches to efibootmgr.c 13 | - network boot entry creation in efi.c 14 | 15 | Joshua Giles 16 | - walk the PCI path inserting parent bridge device path components for 17 | network boot and EDD30 entries. 18 | 19 | Alex Williamson 20 | - Patch to efi.c and efibootmgr.c for handling BootXXXX values 21 | using uppercase hex rather than lowercase, per EFI 1.10 spec. 22 | 23 | Rogerio Timmers 24 | - add option -@ for passing extra variable options in from a file, 25 | necessary for setting up some boot entries for Microsoft Windows. 26 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | rharwood AT redhat DOT com. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 127 | at [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | 135 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) 19yy name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Running 'make' builds the file src/efibootmgr. 2 | efibootmgr should be placed into /usr/sbin/. 3 | 4 | efibootmgr currently requires efivar version 0.19, as well as the pkg-config 5 | utility, to be built. If you receive a message like the following: 6 | 7 | src/include/efi.h:32:20: fatal error: efivar.h: No such file or directory 8 | 9 | then you have not installed these dependencies, which can be found at: 10 | 11 | efivar: https://github.com/rhboot/efivar 12 | pkg-config: https://pkgconfig.freedesktop.org 13 | -------------------------------------------------------------------------------- /MODULE_LICENSE_GPL2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/efibootmgr/0a85e9baaac8a34e4a0bb9c23dfcc9c4f759e061/MODULE_LICENSE_GPL2 -------------------------------------------------------------------------------- /Make.coverity: -------------------------------------------------------------------------------- 1 | COV_EMAIL=$(call get-config,coverity.email) 2 | COV_TOKEN=$(call get-config,coverity.token) 3 | COV_URL=$(call get-config,coverity.url) 4 | COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 5 | 6 | cov-int : clean 7 | cov-build --dir cov-int make all 8 | 9 | cov-clean : 10 | @rm -vf $(NAME)-coverity-*.tar.* 11 | @if [[ -d cov-int ]]; then rm -rf cov-int && echo "removed 'cov-int'"; fi 12 | 13 | cov-file : | $(COV_FILE) 14 | 15 | $(COV_FILE) : cov-int 16 | tar caf $@ cov-int 17 | 18 | cov-upload : 19 | @if [[ -n "$(COV_URL)" ]] && \ 20 | [[ -n "$(COV_TOKEN)" ]] && \ 21 | [[ -n "$(COV_EMAIL)" ]] ; \ 22 | then \ 23 | echo curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ 24 | curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ 25 | else \ 26 | echo Coverity output is in $(COV_FILE) ; \ 27 | fi 28 | 29 | coverity : | cov-test 30 | coverity : cov-file cov-upload 31 | 32 | clean : | cov-clean 33 | 34 | COV_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions cov-build 2>/dev/null) ; [ -n "$$x" ] && echo 1) 35 | ifeq ($(COV_BUILD),) 36 | COV_BUILD_ERROR = $(error cov-build not found) 37 | endif 38 | 39 | cov-test : ; $(COV_BUILD_ERROR) 40 | 41 | .PHONY : coverity cov-upload cov-clean cov-file cov-test 42 | -------------------------------------------------------------------------------- /Make.defaults: -------------------------------------------------------------------------------- 1 | NAME = efibootmgr 2 | prefix ?= /usr 3 | libdir ?= $(prefix)/lib64 4 | datadir ?= $(prefix)/share 5 | mandir ?= $(datadir)/man 6 | includedir ?= $(prefix)/include 7 | bindir ?= $(prefix)/bin 8 | sbindir ?= $(prefix)/sbin 9 | localedir ?= $(datadir)/locale/ 10 | PCDIR ?= $(libdir)/pkgconfig 11 | DESTDIR ?= 12 | ifneq ($(origin EXTRALIBDIRS),undefined) 13 | override EXTRALIBDIR := $(foreach dir,$(EXTRALIBDIRS),-L$(dir)) 14 | endif 15 | ifneq ($(origin EXTRAINCDIRS),undefined) 16 | override EXTRAINCDIR := $(foreach dir,$(EXTRAINCDIRS),-I$(dir)) 17 | endif 18 | 19 | EFIDIR ?= $(shell x=$$(which --skip-alias --skip-functions git 2>/dev/null) ; [ -n "$$x" ] && git config --get efibootmgr.efidir) 20 | ifeq ($(EFIDIR),) 21 | EFIDIR_ERROR = $(error EFIDIR or .gitconfig efibootmgr.efidir must be set to this distro's reserved EFI System Partition subdirectory name) 22 | endif 23 | EFI_LOADER := grub.efi 24 | 25 | INSTALL ?= install 26 | CROSS_COMPILE ?= 27 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config 28 | CC := $(if $(filter default,$(origin CC)),$(CROSS_COMPILE)gcc,$(CC)) 29 | CCLD := $(if $(filter undefined,$(origin CCLD)),$(CC),$(CCLD)) 30 | CFLAGS ?= -O2 -g -flto 31 | AR := $(CROSS_COMPILE)gcc-ar 32 | NM := $(CROSS_COMPILE)gcc-nm 33 | RANLIB := $(CROSS_COMPILE)gcc-ranlib 34 | 35 | PKGS = 36 | 37 | SUBDIR_CFLAGS ?= 38 | clang_cflags = 39 | gcc_cflags = 40 | cflags = $(EXTRALIBDIR) $(EXTRAINCDIR) $(CFLAGS) $(SUBDIR_CFLAGS) \ 41 | -Werror -Wall -Wextra -Wsign-compare -Wstrict-aliasing \ 42 | -std=gnu11 -fPIC \ 43 | -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -DLOCALEDIR=\"$(localedir)\" \ 44 | -DEFIBOOTMGR_VERSION="\"$(VERSION)\"" \ 45 | -DDEFAULT_LOADER=\"\\\\EFI\\\\$(EFIDIR)\\\\$(EFI_LOADER)\" \ 46 | $(if $(findstring clang,$(CC)),$(clang_cflags),) \ 47 | $(if $(findstring gcc,$(CC)),$(gcc_cflags),) \ 48 | $(call pkg-config-cflags) 49 | clang_ccldflags = 50 | gcc_ccldflags = -fno-merge-constants \ 51 | -Wl,--fatal-warnings,--no-allow-shlib-undefined \ 52 | -Wl,-O2 -Wl,--no-undefined-version 53 | ccldflags = $(cflags) $(CCLDFLAGS) $(LDFLAGS) \ 54 | $(if $(findstring clang,$(CCLD)),$(clang_ccldflags),) \ 55 | $(if $(findstring gcc,$(CCLD)),$(gcc_ccldflags),) \ 56 | $(call pkg-config-ldflags) 57 | CPPFLAGS?= 58 | SOFLAGS=-shared 59 | LDLIBS=$(foreach lib,$(LIBS),-l$(lib)) $(call pkg-config-ldlibs) 60 | 61 | .PHONY: check_efidir_error 62 | check_efidir_error : ; $(EFIDIR_ERROR) $(info Building with EFIDIR as $(EFIDIR)) 63 | 64 | COMMIT_ID=$(shell git log -1 --pretty=%H 2>/dev/null || echo master) 65 | -------------------------------------------------------------------------------- /Make.deps: -------------------------------------------------------------------------------- 1 | SRCDIR = $(realpath .) 2 | TOPDIR = $(realpath ..) 3 | 4 | include $(TOPDIR)/Make.version 5 | include $(TOPDIR)/Make.rules 6 | include $(TOPDIR)/Make.defaults 7 | 8 | .%.d : %.c 9 | $(CC) $(cflags) $(CPPFLAGS) -MM -MG -MF $@ $^ 10 | 11 | .%.d : %.S 12 | $(CC) $(cflags) $(CPPFLAGS) -MM -MG -MF $@ $^ 13 | 14 | SOURCES ?= 15 | deps : $(call deps-of,$(filter-out %.h,$(SOURCES))) 16 | -------------------------------------------------------------------------------- /Make.fanalyzer: -------------------------------------------------------------------------------- 1 | GCC_BINARY ?= $(shell x=$$(which --skip-alias --skip-functions gcc 2>/dev/null) ; [ -n "$$x" ] && echo "$$x") 2 | 3 | fanalyzer-test : ; $(if $(findstring /,$(GCC_BINARY)),,$(error gcc not found)) 4 | 5 | fanalyzer : | fanalyzer-test 6 | fanalyzer : clean 7 | $(MAKE) CC=gcc CCACHE_DISABLE=1 CFLAGS="-O2 -g -fanalyzer -Werror=analyzer-null-dereference" 8 | 9 | .PHONY : fanalyzer 10 | -------------------------------------------------------------------------------- /Make.rules: -------------------------------------------------------------------------------- 1 | default : all 2 | 3 | .PHONY: default all deps clean install 4 | 5 | include $(TOPDIR)/Make.version 6 | 7 | all : deps 8 | 9 | deps : 10 | 11 | clean : 12 | 13 | install : 14 | 15 | %.a : %.so 16 | $(AR) -cvqs $@ $^ 17 | 18 | % : %.c 19 | 20 | % : | %.c 21 | $(CCLD) $(ccldflags) $(CPPFLAGS) -o $@ $^ $(LDLIBS) 22 | 23 | %-static : %.c 24 | $(CCLD) $(ccldflags) $(CPPFLAGS) -o $@ $^ $(LDLIBS) 25 | 26 | %.so : 27 | $(CCLD) $(ccldflags) $(CPPFLAGS) $(SOFLAGS) \ 28 | -Wl,-soname,$@.$(VERSION) \ 29 | -o $@ $^ $(LDLIBS) 30 | 31 | %.o : %.c 32 | $(CC) $(cflags) $(CPPFLAGS) -c -o $@ $(filter %.c %.o %.S,$^) 33 | 34 | %.S: %.c 35 | $(CC) $(cflags) $(CPPFLAGS) -S $< -o $@ 36 | 37 | %.E: %.c 38 | $(CC) $(cflags) $(CPPFLAGS) -E $< -o $@ 39 | 40 | %.c : %.h 41 | 42 | .%.d : 43 | 44 | define substitute-version = 45 | sed \ 46 | -e "s,@@VERSION@@,$(VERSION),g" \ 47 | -e "s,@@LIBDIR@@,$(libdir),g" \ 48 | -e "s,@@LIBEXECDIR@@,$(libexecdir),g" \ 49 | -e "s,@@EFIDIR@@,$(EFIDIR),g" \ 50 | -e "s,@@EFI_LOADER@@,$(EFI_LOADER),g" \ 51 | -e 's,@@DEFAULT_LOADER@@,\\\\EFI\\\\$(EFIDIR)\\\\$(EFI_LOADER),g' \ 52 | $(1) > $(2) 53 | endef 54 | 55 | %.8 : %.8.in 56 | @$(call substitute-version,$<,$@) 57 | 58 | %.pc : %.pc.in 59 | @$(call substitute-version,$<,$@) 60 | 61 | %.spec : %.spec.in 62 | @$(call substitute-version,$<,$@) 63 | 64 | pkg-config-cflags = \ 65 | $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --cflags $(PKGS); fi) 66 | pkg-config-ldflags = \ 67 | $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --libs-only-L --libs-only-other $(PKGS) ; fi) 68 | pkg-config-ldlibs = \ 69 | $(shell if [ -n "$(PKGS)" ]; then $(PKG_CONFIG) --libs-only-l $(PKGS) ; fi) 70 | 71 | objects-of = \ 72 | $(patsubst %.c,%.o,$(1)) 73 | 74 | define deps-of = 75 | $(foreach src,$(filter %.c,$(1)),$(patsubst %.c,.%.d,$(src))) \ 76 | $(foreach src,$(filter %.S,$(1)),$(patsubst %.S,.%.d,$(src))) 77 | endef 78 | 79 | define get-config = 80 | $(shell git config --local --get "efibootmgr.$(1)") 81 | endef 82 | -------------------------------------------------------------------------------- /Make.scan-build: -------------------------------------------------------------------------------- 1 | SCAN_BUILD ?= $(shell x=$$(which --skip-alias --skip-functions scan-build 2>/dev/null) ; [ -n "$$x" ] && echo 1) 2 | ifeq ($(SCAN_BUILD),) 3 | SCAN_BUILD_ERROR = $(error scan-build not found) 4 | endif 5 | 6 | scan-test : ; $(SCAN_BUILD_ERROR) 7 | 8 | scan-clean : clean 9 | @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi 10 | 11 | scan-build : | scan-test 12 | scan-build : clean 13 | scan-build -o scan-results make $(DASHJ) CC=clang all 14 | 15 | scan-build-all : | scan-test 16 | scan-build-all : clean 17 | scan-build -o scan-results make $(DASHJ) CC=clang all 18 | 19 | .PHONY : scan-build scan-clean 20 | 21 | -------------------------------------------------------------------------------- /Make.version: -------------------------------------------------------------------------------- 1 | VERSION=18 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOPDIR = $(shell echo $$PWD) 2 | 3 | include $(TOPDIR)/Make.version 4 | include $(TOPDIR)/Make.rules 5 | include $(TOPDIR)/Make.defaults 6 | include $(TOPDIR)/Make.coverity 7 | include $(TOPDIR)/Make.fanalyzer 8 | include $(TOPDIR)/Make.scan-build 9 | 10 | SUBDIRS := src 11 | 12 | all install deps : | check_efidir_error Make.version 13 | @set -e ; for x in $(SUBDIRS) ; do \ 14 | $(MAKE) -C $$x $@ ; \ 15 | done 16 | 17 | clean : | check_efidir_error Make.version 18 | @set -e ; for x in $(SUBDIRS) ; do \ 19 | $(MAKE) -C $$x $@ ; \ 20 | done 21 | 22 | all : efibootmgr.spec 23 | 24 | efibootmgr efibootmgr-static : 25 | $(MAKE) -C src $@ 26 | 27 | $(SUBDIRS) : 28 | $(MAKE) -C $@ 29 | 30 | .PHONY: $(SUBDIRS) 31 | 32 | efibootmgr.spec : | Makefile Make.version 33 | 34 | distclean : 35 | $(MAKE) clean 36 | @rm -vf efibootmgr.spec 37 | 38 | GITTAG = $(shell bash -c "echo $$(($(VERSION) + 1))") 39 | 40 | test-archive: efibootmgr.spec 41 | @rm -rf /tmp/efibootmgr-$(GITTAG) /tmp/efibootmgr-$(GITTAG)-tmp 42 | @mkdir -p /tmp/efibootmgr-$(GITTAG)-tmp 43 | @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; tar x ) 44 | @git diff | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; patch -s -p1 -b -z .gitdiff ) 45 | @mv /tmp/efibootmgr-$(GITTAG)-tmp/ /tmp/efibootmgr-$(GITTAG)/ 46 | @cp efibootmgr.spec /tmp/efibootmgr-$(GITTAG)/ 47 | @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/efibootmgr-$(GITTAG).tar.bz2 efibootmgr-$(GITTAG) 48 | @rm -rf /tmp/efibootmgr-$(GITTAG) 49 | @echo "The archive is in efibootmgr-$(GITTAG).tar.bz2" 50 | 51 | bumpver : 52 | @echo VERSION=$(GITTAG) > Make.version 53 | @git add Make.version 54 | git commit -m "Bump version to $(GITTAG)" -s 55 | 56 | tag: 57 | git tag -s $(GITTAG) refs/heads/main 58 | 59 | archive: bumpver tag efibootmgr.spec 60 | @rm -rf /tmp/efibootmgr-$(GITTAG) /tmp/efibootmgr-$(GITTAG)-tmp 61 | @mkdir -p /tmp/efibootmgr-$(GITTAG)-tmp 62 | @git archive --format=tar $(GITTAG) | ( cd /tmp/efibootmgr-$(GITTAG)-tmp/ ; tar x ) 63 | @mv /tmp/efibootmgr-$(GITTAG)-tmp/ /tmp/efibootmgr-$(GITTAG)/ 64 | @cp efibootmgr.spec /tmp/efibootmgr-$(GITTAG)/ 65 | @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/efibootmgr-$(GITTAG).tar.bz2 efibootmgr-$(GITTAG) 66 | @rm -rf /tmp/efibootmgr-$(GITTAG) 67 | @echo "The archive is in efibootmgr-$(GITTAG).tar.bz2" 68 | 69 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | COPYING -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is efibootmgr, a Linux user-space application to modify the Intel 2 | Extensible Firmware Interface (EFI) Boot Manager. This application 3 | can create and destroy boot entries, change the boot order, change 4 | the next running boot option, and more. 5 | 6 | Details on the EFI Boot Manager are available from the EFI 7 | Specification, v1.02 or above, available from: http://www.uefi.org 8 | 9 | Note: efibootmgr requires either the efivarfs or the 10 | legacy efivars kernel module to be loaded prior to use. 11 | 12 | usage: efibootmgr [options] 13 | -a | --active Set bootnum active. 14 | -A | --inactive Set bootnum inactive. 15 | -b | --bootnum XXXX Modify BootXXXX (hex). 16 | -B | --delete-bootnum Delete bootnum. 17 | -c | --create Create new variable bootnum and add to bootorder. 18 | -d | --disk disk (Defaults to /dev/sda) containing loader. 19 | -e | --edd [1|3|-1] Force EDD 1.0 or 3.0 creation variables, or guess. 20 | -E | --device num EDD 1.0 device number (defaults to 0x80). 21 | -f | --reconnect Re-connect devices after driver is loaded. 22 | -F | --no-reconnect Do not re-connect devices after driver is loaded. 23 | -g | --gpt Force disk w/ invalid PMBR to be treated as GPT. 24 | -i | --iface name Create a netboot entry for the named interface. 25 | -l | --loader name (Defaults to \elilo.efi). 26 | -L | --label label Boot manager display label (defaults to "Linux"). 27 | -n | --bootnext XXXX Set BootNext to XXXX (hex). 28 | -N | --delete-bootnext Delete BootNext. 29 | -o | --bootorder XXXX,YYYY,ZZZZ,... Explicitly set BootOrder (hex). 30 | -O | --delete-bootorder Delete BootOrder. 31 | -p | --part part (Defaults to 1) containing loader. 32 | -q | --quiet Be quiet. 33 | -t | --timeout seconds Boot manager timeout. 34 | -T | --delete-timeout Delete Timeout value. 35 | -u | --unicode | --UCS-2 Pass extra args as UCS-2 (default is ASCII). 36 | -v | --verbose Print additional information. 37 | -V | --version Return version and exit. 38 | -@ | --append-binary-args Append extra variable args from 39 | file (use - to read from stdin). 40 | 41 | 42 | Typical usage: 43 | 44 | Root can use it to display the current Boot Manager settings. 45 | 46 | [root@localhost ~]# efibootmgr 47 | BootCurrent: 0004 48 | BootNext: 0003 49 | BootOrder: 0004,0000,0001,0002,0003 50 | Timeout: 30 seconds 51 | Boot0000* Diskette Drive(device:0) 52 | Boot0001* CD-ROM Drive(device:FF) 53 | Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) 54 | Boot0003* PXE Boot: MAC(00D0B7C15D91) 55 | Boot0004* Linux 56 | 57 | This shows: 58 | BootCurrent - the boot entry used to start the currently running 59 | system. 60 | 61 | BootOrder - the boot order as would appear in the boot manager. The 62 | boot manager tries to boot the first active entry on this list. If 63 | unsuccessful, it tries the next entry, and so on. 64 | 65 | BootNext - the boot entry which is scheduled to be run on next boot. 66 | This supersedes BootOrder for one boot only, and is deleted by the 67 | boot manager after first use. This allows you to change the next boot 68 | behavior without changing BootOrder. 69 | 70 | Timeout - the time in seconds between when the boot manager appears 71 | on the screen until when it automatically chooses the startup value 72 | from BootNext or BootOrder. 73 | 74 | Five boot entries (0000 - 0004), the active/inactive flag (* means 75 | active), and the name displayed on the screen. 76 | 77 | Alternative use cases could be as follows: 78 | 79 | 1) An OS installer would call 'efibootmgr -c'. This assumes that 80 | /dev/sda1 is your EFI System Partition, and is mounted at /boot/efi. 81 | This creates a new boot option, called "Linux", and puts it at the top 82 | of the boot order list. Options may be passed to modify the 83 | default behavior. The default OS Loader is elilo.efi. 84 | 85 | 2) A system administrator wants to change the boot order. She would 86 | call 'efibootmgr -o 3,4' to specify PXE boot first, then Linux 87 | boot. 88 | 89 | 3) A system administrator wants to change the boot order for the next 90 | boot only. She would call 'efibootmgr -n 4' to specify that the 91 | Linux entry be taken on next boot. 92 | 93 | 4) A system administrator wants to delete the Linux boot option from 94 | the menu. 'efibootmgr -b 4 -B' deletes entry 4 and removes it 95 | from BootOrder. 96 | 97 | 5) A system administrator wants to create a boot option to network 98 | boot (PXE). You create the boot entry with: 99 | 'efibootmgr -c -i eth0 -L netboot' 100 | 101 | Please direct any bugs, features, patches, etc. to the Red Hat bootloader 102 | team: 103 | 104 | https://github.com/rhboot/efibootmgr 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is **efibootmgr**, a Linux user-space application to modify the Intel 2 | Extensible Firmware Interface (EFI) Boot Manager. This application 3 | can create and destroy boot entries, change the boot order, change 4 | the next running boot option, and more. 5 | 6 | Details on the EFI Boot Manager are available from the EFI 7 | Specification, v1.02 or above, available from: http://www.uefi.org 8 | 9 | Note: efibootmgr requires either the efivarfs or the 10 | legacy efivars kernel module to be loaded prior to use. 11 | 12 | ``` 13 | usage: efibootmgr [options] 14 | -a | --active Set bootnum active. 15 | -A | --inactive Set bootnum inactive. 16 | -b | --bootnum XXXX Modify BootXXXX (hex). 17 | -B | --delete-bootnum Delete bootnum. 18 | -c | --create Create new variable bootnum and add to bootorder. 19 | -d | --disk disk (Defaults to /dev/sda) containing loader. 20 | -e | --edd [1|3|-1] Force EDD 1.0 or 3.0 creation variables, or guess. 21 | -E | --device num EDD 1.0 device number (defaults to 0x80). 22 | -f | --reconnect Re-connect devices after driver is loaded. 23 | -F | --no-reconnect Do not re-connect devices after driver is loaded. 24 | -g | --gpt Force disk w/ invalid PMBR to be treated as GPT. 25 | -i | --iface name Create a netboot entry for the named interface. 26 | -l | --loader name (Defaults to \elilo.efi). 27 | -L | --label label Boot manager display label (defaults to "Linux"). 28 | -n | --bootnext XXXX Set BootNext to XXXX (hex). 29 | -N | --delete-bootnext Delete BootNext. 30 | -o | --bootorder XXXX,YYYY,ZZZZ,... Explicitly set BootOrder (hex). 31 | -O | --delete-bootorder Delete BootOrder. 32 | -p | --part part (Defaults to 1) containing loader. 33 | -q | --quiet Be quiet. 34 | -t | --timeout seconds Boot manager timeout. 35 | -T | --delete-timeout Delete Timeout value. 36 | -u | --unicode | --UCS-2 Pass extra args as UCS-2 (default is ASCII). 37 | -v | --verbose Print additional information. 38 | -V | --version Return version and exit. 39 | -@ | --append-binary-args Append extra variable args from 40 | file (use - to read from stdin). 41 | ``` 42 | 43 | Typical usage: 44 | 45 | Root can use it to display the current Boot Manager settings. 46 | ``` 47 | [root@localhost ~]# efibootmgr 48 | BootCurrent: 0004 49 | BootNext: 0003 50 | BootOrder: 0004,0000,0001,0002,0003 51 | Timeout: 30 seconds 52 | Boot0000* Diskette Drive(device:0) 53 | Boot0001* CD-ROM Drive(device:FF) 54 | Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) 55 | Boot0003* PXE Boot: MAC(00D0B7C15D91) 56 | Boot0004* Linux 57 | ``` 58 | This shows: 59 | **BootCurrent** - the boot entry used to start the currently running 60 | system. 61 | 62 | **BootOrder** - the boot order as would appear in the boot manager. The 63 | boot manager tries to boot the first active entry on this list. If 64 | unsuccessful, it tries the next entry, and so on. 65 | 66 | **BootNext** - the boot entry which is scheduled to be run on next boot. 67 | This supersedes BootOrder for one boot only, and is deleted by the 68 | boot manager after first use. This allows you to change the next boot 69 | behavior without changing BootOrder. 70 | 71 | **Timeout** - the time in seconds between when the boot manager appears 72 | on the screen until when it automatically chooses the startup value 73 | from BootNext or BootOrder. 74 | 75 | Five boot entries (0000 - 0004), the active/inactive flag (* means 76 | active), and the name displayed on the screen. 77 | 78 | Alternative use cases could be as follows: 79 | 80 | 1) An OS installer would call `efibootmgr -c`. This assumes that 81 | /dev/sda1 is your EFI System Partition, and is mounted at /boot/efi. 82 | This creates a new boot option, called "Linux", and puts it at the top 83 | of the boot order list. Options may be passed to modify the 84 | default behavior. The default OS Loader is elilo.efi. 85 | 86 | 2) A system administrator wants to change the boot order. She would 87 | call `efibootmgr -o 3,4` to specify PXE boot first, then Linux 88 | boot. 89 | 90 | 3) A system administrator wants to change the boot order for the next 91 | boot only. She would call `efibootmgr -n 4` to specify that the 92 | Linux entry be taken on next boot. 93 | 94 | 4) A system administrator wants to delete the Linux boot option from 95 | the menu. `efibootmgr -b 4 -B` deletes entry 4 and removes it 96 | from BootOrder. 97 | 98 | 5) A system administrator wants to create a boot option to network 99 | boot (PXE). You create the boot entry with: 100 | `efibootmgr -c -i eth0 -L netboot` 101 | 102 | Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr . 103 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - support for setting hotkeys 2 | - support for driver variables, not just boot variables. 3 | - support for arbitrary device paths with -c (including guid parsing) 4 | - maintain a list of known GUIDs and print {vendor} instead of the full GUID 5 | - accept these for arbitrary paths as well. 6 | - these are done in libefivar, but efibootmgr still needs some work on it 7 | - MS-DOS style extended partitions 8 | - lots more network stuff 9 | - IPv6 with various discovery methods 10 | - IPv4 w/o dhcp 11 | - make sure FCoE works 12 | - iscsi 13 | - make sure nvme works 14 | -------------------------------------------------------------------------------- /efibootmgr.spec.in: -------------------------------------------------------------------------------- 1 | Summary: EFI Boot Manager 2 | Name: efibootmgr 3 | Version: @@VERSION@@ 4 | Release: 1%{?dist} 5 | Group: System Environment/Base 6 | License: GPLv2+ 7 | URL: https://github.com/rhboot/%{name}/ 8 | BuildRequires: git, popt-devel 9 | BuildRequires: efivar-libs >= 30-1, efivar-devel >= 30-1 10 | BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXXXX) 11 | # EFI/UEFI don't exist on PPC 12 | ExclusiveArch: %{ix86} x86_64 aarch64 arm 13 | 14 | # for RHEL / Fedora when efibootmgr was part of the elilo package 15 | Conflicts: elilo <= 3.6-6 16 | Obsoletes: elilo <= 3.6-6 17 | 18 | Source0: https://github.com/rhboot/%{name}/releases/download/%{name}-%{version}/%{name}-%{version}.tar.bz2 19 | 20 | %description 21 | %{name} displays and allows the user to edit the UEFI Boot Manager 22 | variables. Additional information about UEFI can be found at 23 | http://www.uefi.org 24 | 25 | %prep 26 | %setup -q 27 | git init 28 | git config user.email "example@example.com" 29 | git config user.name "RHEL Ninjas" 30 | git add . 31 | git commit -a -q -m "%{version} baseline." 32 | git am %{patches} - 14-1 56 | - Update to efibootmgr 14 57 | - Remove "(hex)" from description of --delete-bootnum 58 | - Fix a typo in the popt options 59 | - Add README.md 60 | - make efibootdump install by default 61 | - Man page fixes 62 | - Better compiler detection 63 | - Don't use --default-symver in efibootmgr 64 | - Make -flto part of the overrideable CFLAGS 65 | 66 | * Wed Aug 17 2016 Peter Jones - 13-1 67 | - Update to efibootmgr 13 68 | - Add support for --sysprep and --driver to support UEFI System Prep 69 | Applications and UEFI Drivers. 70 | - use efivar's error reporting facility, and show error traces when 71 | "-v -v" is used. 72 | - Still yet better error codes returned on failures. 73 | - Add -m and -M to support Memory Address Range Mirroring. 74 | - Add efibootdump, to examine Boot* variables found in tarballs in bug 75 | reports and similar. 76 | - miscellaneous bugfixes. 77 | 78 | * Thu May 28 2015 Peter Jones - 0.12-1 79 | - Update to 0.12 80 | - use libefiboot and libefivar to make device paths and load options 81 | - don't depend on -lz or -lpci any more 82 | 83 | * Tue Oct 21 2014 Peter Jones - 0.11.0-1 84 | - Fix "-n" and friends not being assigned/checked right sometimes from 0.10.0-1 85 | - Generate more archives to avoid people using github's, because they're just 86 | bad. 87 | 88 | * Mon Oct 20 2014 Peter Jones - 0.10.0-1 89 | - Make -o parameter validation work better and be more informative 90 | - Better exit values 91 | - Fix a segfault with appending ascii arguments. 92 | 93 | * Tue Sep 09 2014 Peter Jones - 0.8.0-1 94 | - Release 0.8.0 95 | 96 | * Mon Jan 13 2014 Peter Jones - 0.6.1-1 97 | - Release 0.6.1 98 | 99 | * Mon Jan 13 2014 Jared Dominguez 100 | - new home https://github.com/vathpela/efibootmgr 101 | 102 | * Thu Jan 3 2008 Matt Domsch 0.5.4-1 103 | - split efibootmgr into its own RPM for Fedora/RHEL. 104 | 105 | * Tue Aug 24 2004 Matt Domsch 106 | - new home linux.dell.com 107 | 108 | * Fri May 18 2001 Matt Domsch 109 | - See doc/ChangeLog 110 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.8 2 | efibootmgr 3 | eficonman 4 | efibootnext 5 | efibootdump 6 | -------------------------------------------------------------------------------- /src/Android.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2019 The Android-x86 Open Source Project 3 | # 4 | # Licensed under the GNU General Public License Version 2 or later. 5 | # You may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.gnu.org/licenses/gpl.html 9 | # 10 | 11 | LOCAL_PATH := $(call my-dir) 12 | 13 | include $(CLEAR_VARS) 14 | 15 | include $(LOCAL_PATH)/../Make.version 16 | 17 | LOCAL_MODULE := efibootmgr 18 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 19 | LOCAL_STATIC_LIBRARIES := libefivar 20 | LOCAL_MODULE_PATH := $(TARGET_INSTALLER_OUT)/sbin 21 | 22 | LOCAL_CFLAGS := \ 23 | -Werror -Wall -Wextra -Wsign-compare -Wstrict-aliasing \ 24 | -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE \ 25 | -DEFIBOOTMGR_VERSION="\"$(VERSION)\"" \ 26 | -DDEFAULT_LOADER=\"\\\\elilo.efi\" 27 | 28 | LOCAL_SRC_FILES := \ 29 | efi.c \ 30 | efibootmgr.c \ 31 | parse_loader_data.c 32 | 33 | include $(BUILD_EXECUTABLE) 34 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR = $(realpath .) 2 | TOPDIR = $(realpath ..) 3 | 4 | include $(TOPDIR)/Make.version 5 | include $(TOPDIR)/Make.rules 6 | include $(TOPDIR)/Make.defaults 7 | 8 | BINTARGETS=efibootmgr efibootdump 9 | TARGETS=$(BINTARGETS) efibootmgr.8 efibootdump.8 10 | 11 | all : deps $(TARGETS) 12 | 13 | EFIBOOTMGR_SOURCES = efibootmgr.c efi.c parse_loader_data.c 14 | EFICONMAN_SOURCES = eficonman.c 15 | EFIBOOTDUMP_SOURCES = efibootdump.c parse_loader_data.c 16 | EFIBOOTNEXT_SOURCES = efibootnext.c 17 | ALL_SOURCES=$(EFIBOOTMGR_SOURCES) 18 | -include $(call deps-of,$(ALL_SOURCES)) 19 | 20 | efibootmgr : $(call objects-of,$(EFIBOOTMGR_SOURCES)) 21 | efibootmgr : PKGS=efivar efiboot 22 | 23 | eficonman : $(call objects-of,$(EFICONMAN_SOURCES)) 24 | eficonman : PKGS=efivar efiboot popt 25 | 26 | efibootdump : $(call objects-of,$(EFIBOOTDUMP_SOURCES)) 27 | efibootdump : PKGS=efivar efiboot popt 28 | 29 | efibootnext : $(call objects-of,$(EFIBOOTNEXT_SOURCES)) 30 | efibootnext : PKGS=efivar efiboot popt 31 | 32 | deps : PKGS=efivar efiboot popt 33 | deps : $(ALL_SOURCES) 34 | $(MAKE) -f $(TOPDIR)/Make.deps \ 35 | SOURCES="$(ALL_SOURCES)" \ 36 | PKGS="$(PKGS)" \ 37 | deps 38 | 39 | clean : 40 | @rm -rfv *.o *.a *.so $(TARGETS) 41 | @rm -rfv .*.d 42 | 43 | install : $(TARGETS) 44 | $(INSTALL) -d -m 755 $(DESTDIR)/$(sbindir)/ 45 | $(INSTALL) -m 755 efibootmgr $(DESTDIR)/$(sbindir)/efibootmgr 46 | $(INSTALL) -m 755 efibootdump $(DESTDIR)/$(sbindir)/efibootdump 47 | $(INSTALL) -d -m 755 $(DESTDIR)/$(mandir)/man8/ 48 | $(INSTALL) -m 644 efibootmgr.8 $(DESTDIR)/$(mandir)/man8/efibootmgr.8 49 | $(INSTALL) -m 644 efibootdump.8 $(DESTDIR)/$(mandir)/man8/efibootdump.8 50 | 51 | .PHONY : all deps clean install 52 | -------------------------------------------------------------------------------- /src/efi.c: -------------------------------------------------------------------------------- 1 | /* 2 | efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars 3 | 4 | Copyright (C) 2001,2003 Dell Computer Corporation 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include "fix_coverity.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "efi.h" 46 | #include "efibootmgr.h" 47 | #include "list.h" 48 | 49 | static int 50 | select_var_names_by_prefix(const efi_guid_t *guid, const char *prefix, 51 | const char *name) 52 | { 53 | efi_guid_t global = EFI_GLOBAL_GUID; 54 | size_t plen = strlen(prefix); 55 | const char *num = name + plen; 56 | if (!strncmp(name, prefix, plen) && 57 | isxdigit(num[0]) && isxdigit(num[1]) && 58 | isxdigit(num[2]) && isxdigit(num[3]) && 59 | !memcmp(guid, &global, sizeof (global))) 60 | return 1; 61 | return 0; 62 | } 63 | typedef __typeof__(select_var_names_by_prefix) filter_t; 64 | 65 | static int 66 | cmpstringp(const void *p1, const void *p2) 67 | { 68 | const char *s1 = *(const char **)p1; 69 | const char *s2 = *(const char **)p2; 70 | return strcoll(s1, s2); 71 | } 72 | 73 | static int 74 | read_prefixed_var_names(filter_t filter, const char *prefix, char ***namelist) 75 | { 76 | int rc; 77 | efi_guid_t *guid = NULL; 78 | char *name = NULL; 79 | char **newlist = NULL; 80 | int nentries = 0; 81 | int i; 82 | 83 | rc = efi_variables_supported(); 84 | if (!rc) 85 | return -1; 86 | 87 | while ((rc = efi_get_next_variable_name(&guid, &name)) > 0) { 88 | if (!filter(guid, prefix, name)) 89 | continue; 90 | 91 | char *aname = strdup(name); 92 | if (!aname) { 93 | rc = -1; 94 | break; 95 | } 96 | 97 | char **tmp = realloc(newlist, (++nentries + 1) * sizeof (*newlist)); 98 | if (!tmp) { 99 | free(aname); 100 | rc = -1; 101 | break; 102 | } 103 | 104 | tmp[nentries] = NULL; 105 | tmp[nentries-1] = aname; 106 | 107 | newlist = tmp; 108 | } 109 | if (rc == 0 && newlist) { 110 | qsort(newlist, nentries, sizeof (char *), cmpstringp); 111 | *namelist = newlist; 112 | } else { 113 | if (newlist) { 114 | for (i = 0; newlist[i] != NULL; i++) 115 | free(newlist[i]); 116 | free(newlist); 117 | } 118 | } 119 | return rc; 120 | } 121 | 122 | int 123 | read_var_names(const char *prefix, char ***namelist) 124 | { 125 | return read_prefixed_var_names(select_var_names_by_prefix, 126 | prefix, namelist); 127 | } 128 | 129 | int 130 | read_boot_var_names(char ***namelist) 131 | { 132 | return read_var_names("Boot", namelist); 133 | } 134 | 135 | static int 136 | get_path_options(void) 137 | { 138 | switch (opts.abbreviate_path) 139 | { 140 | case EFIBOOTMGR_PATH_ABBREV_EDD10: 141 | /* EDD 1.0 */ 142 | return EFIBOOT_ABBREV_EDD10; 143 | case EFIBOOTMGR_PATH_ABBREV_NONE: 144 | /* EDD 3.0+, which we actually just ignore, because we don't 145 | * actually *have* edd, and can't actually derive a path from 146 | * anything. */ 147 | return EFIBOOT_ABBREV_NONE; 148 | case EFIBOOTMGR_PATH_ABBREV_FILE: 149 | /* Abbreviate to a file path */ 150 | return EFIBOOT_ABBREV_FILE; 151 | case EFIBOOTMGR_PATH_ABBREV_UNSPECIFIED: 152 | case EFIBOOTMGR_PATH_ABBREV_HD: 153 | default: 154 | /* Abbreviate to an HD path */ 155 | return EFIBOOT_ABBREV_HD; 156 | } 157 | } 158 | 159 | /** 160 | * make_linux_load_option() 161 | * @data - load option returned 162 | * *data_size - load option size returned 163 | * 164 | * Returns 0 on error, length of load option created on success. 165 | */ 166 | ssize_t 167 | make_linux_load_option(uint8_t **data, size_t *data_size, 168 | uint8_t *optional_data, size_t optional_data_size) 169 | { 170 | ssize_t needed; 171 | uint32_t attributes = opts.active ? LOAD_OPTION_ACTIVE : 0 172 | | (opts.reconnect > 0 ? LOAD_OPTION_FORCE_RECONNECT : 0); 173 | int saved_errno; 174 | efidp dp = NULL; 175 | 176 | if (opts.iface && opts.ip_version == EFIBOOTMGR_IPV4) { 177 | needed = efi_generate_ipv4_device_path(NULL, 0, opts.iface, 178 | opts.local_ip_addr, 179 | opts.remote_ip_addr, 180 | opts.gateway_ip_addr, 181 | opts.ip_netmask, 182 | opts.ip_local_port, 183 | opts.ip_remote_port, 184 | opts.ip_protocol, 185 | opts.ip_addr_origin); 186 | if (needed < 0) { 187 | efi_error("efi_generate_ipv4_device_path() = %zd (failed)", 188 | needed); 189 | return -1; 190 | } 191 | if (data_size && *data_size) { 192 | dp = malloc(needed); 193 | 194 | needed = efi_generate_ipv4_device_path( 195 | (uint8_t *)dp, needed, 196 | opts.iface, 197 | opts.local_ip_addr, 198 | opts.remote_ip_addr, 199 | opts.gateway_ip_addr, 200 | opts.ip_netmask, 201 | opts.ip_local_port, 202 | opts.ip_remote_port, 203 | opts.ip_protocol, 204 | opts.ip_addr_origin); 205 | if (needed < 0) { 206 | free(dp); 207 | efi_error("efi_generate_ipv4_device_path() = %zd (failed)", 208 | needed); 209 | return -1; 210 | } 211 | } 212 | } else if (opts.iface && opts.ip_version == EFIBOOTMGR_IPV6) { 213 | errno = ENOSYS; 214 | return -1; 215 | } else { 216 | uint32_t options; 217 | 218 | options = get_path_options(); 219 | 220 | needed = efi_generate_file_device_path_from_esp(NULL, 0, 221 | opts.disk, opts.part, 222 | opts.loader, options, 223 | opts.edd10_devicenum); 224 | if (needed < 0) { 225 | efi_error("efi_generate_file_device_path_from_esp() = %zd (failed)", 226 | needed); 227 | return -1; 228 | } 229 | 230 | if (data_size && *data_size) { 231 | dp = malloc(needed); 232 | if (dp == NULL) 233 | return -1; 234 | needed = efi_generate_file_device_path_from_esp( 235 | (uint8_t *)dp, needed, 236 | opts.disk, opts.part, 237 | opts.loader, options, 238 | opts.edd10_devicenum); 239 | if (needed < 0) { 240 | efi_error("efi_generate_file_device_path_from_esp() = %zd (failed)", 241 | needed); 242 | free(dp); 243 | return -1; 244 | } 245 | } 246 | } 247 | 248 | size_t data_size_tmp = 0; 249 | if (data_size) 250 | data_size_tmp = *data_size; 251 | needed = efi_loadopt_create(*data, data_size_tmp, 252 | attributes, dp, needed, opts.label, 253 | optional_data, optional_data_size); 254 | if (dp) { 255 | saved_errno = errno; 256 | free(dp); 257 | dp = NULL; 258 | errno = saved_errno; 259 | } 260 | if (needed < 0) { 261 | efi_error("efi_loadopt_create() = %zd (failed)", needed); 262 | return -1; 263 | } 264 | 265 | return needed; 266 | } 267 | 268 | static ssize_t 269 | read_stdin(uint8_t *data_out, ssize_t data_size_out) 270 | { 271 | static uint8_t *data = NULL; 272 | static ssize_t data_size = 0; 273 | off_t pos = 0; 274 | ssize_t allocated; 275 | 276 | if (data_out && data_size_out) { 277 | if (!data || data_size != data_size_out) { 278 | errno = EINVAL; 279 | return -1; 280 | } 281 | memcpy(data_out, data, data_size); 282 | return data_size; 283 | } 284 | allocated = 4096; 285 | data = malloc(allocated); 286 | if (!data) 287 | return -1; 288 | memset(data, 0, allocated); 289 | 290 | while (1) { 291 | ssize_t ret; 292 | if (allocated - pos == 0) { 293 | allocated += 4096; 294 | /* 295 | * there's really no way a variable is going to be 296 | * 64k and work, so bail before we suck up all of 297 | * memory. 298 | */ 299 | if (allocated > 4096 * 16) { 300 | errno = ENOSPC; 301 | err: 302 | free(data); 303 | data = 0; 304 | data_size = 0; 305 | return -1; 306 | } 307 | 308 | uint8_t *data_new; 309 | data_new = realloc(data, allocated); 310 | if (!data_new) 311 | goto err; 312 | data = data_new; 313 | } 314 | ret = fread(data+pos, 1, allocated-pos, stdin); 315 | if (ret == 0) { 316 | if (ferror(stdin)) { 317 | errno = EIO; 318 | goto err; 319 | } 320 | if (feof(stdin)) 321 | break; 322 | } 323 | data_size += ret; 324 | pos += ret; 325 | } 326 | return data_size; 327 | } 328 | 329 | ssize_t 330 | get_extra_args(uint8_t *data, ssize_t data_size) 331 | { 332 | int i; 333 | ssize_t needed = 0, sz; 334 | off_t off = 0; 335 | 336 | if (opts.extra_opts_file) { 337 | if (!strcmp(opts.extra_opts_file, "-")) 338 | needed = read_stdin(data, data_size); 339 | else 340 | needed = efi_loadopt_args_from_file(data, data_size, 341 | opts.extra_opts_file); 342 | if (needed < 0) 343 | fprintf(stderr, "efibootmgr: get_extra_args: %m\n"); 344 | return needed; 345 | } 346 | for (i = opts.optind; i < opts.argc; i++) { 347 | int space = (i < opts.argc - 1) ? 1 : 0; 348 | 349 | if (opts.unicode) { 350 | sz = efi_loadopt_args_as_ucs2( 351 | (uint16_t *)(data+off), 352 | data_size?data_size+off:0, 353 | (uint8_t *)opts.argv[i]); 354 | if (sz < 0) 355 | return -1; 356 | off += sz; 357 | if (data && off < data_size-2 && space) { 358 | data[off] = '\0'; 359 | data[off+1] = '\0'; 360 | } 361 | off += space * sizeof (uint16_t); 362 | } else { 363 | sz = efi_loadopt_args_as_utf8(data+off, 364 | data_size?data_size+off:0, 365 | (uint8_t *)opts.argv[i]); 366 | if (sz < 0) 367 | return -1; 368 | off += sz; 369 | if (data && off < data_size-1 && space) { 370 | data[off] = '\0'; 371 | } 372 | off += space; 373 | } 374 | needed += off; 375 | } 376 | return needed; 377 | } 378 | -------------------------------------------------------------------------------- /src/efi.h: -------------------------------------------------------------------------------- 1 | /* 2 | efi.[ch] - Extensible Firmware Interface definitions 3 | 4 | Copyright (C) 2001, 2003 Dell Computer Corporation 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #pragma once 22 | 23 | /* 24 | * Extensible Firmware Interface 25 | * Based on 'Extensible Firmware Interface Specification' 26 | * version 1.02, 12 December, 2000 27 | */ 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | /******************************************************* 34 | * Boot Option Attributes 35 | *******************************************************/ 36 | #define LOAD_OPTION_ACTIVE 0x00000001 37 | #define LOAD_OPTION_FORCE_RECONNECT 0x00000002 38 | #define LOAD_OPTION_HIDDEN 0x00000008 39 | 40 | #define LOAD_OPTION_CATEGORY_MASK 0x00001f00 41 | #define LOAD_OPTION_CATEGORY_BOOT 0x00000000 42 | #define LOAD_OPTION_CATEGORY_APP 0x00000100 43 | 44 | /******************************************************* 45 | * GUIDs 46 | *******************************************************/ 47 | #define BLKX_UNKNOWN_GUID \ 48 | EFI_GUID( 0x47c7b225, 0xc42a, 0x11d2, 0x8e57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) 49 | #define ADDRESS_RANGE_MIRROR_VARIABLE_GUID \ 50 | EFI_GUID( 0x7b9be2e0, 0xe28a, 0x4197, 0xad3e, 0x32, 0xf0, 0x62, 0xf9, 0x46, 0x2c) 51 | 52 | /* Exported functions */ 53 | 54 | extern int read_boot_var_names(char ***namelist); 55 | extern int read_var_names(const char *prefix, char ***namelist); 56 | extern ssize_t make_linux_load_option(uint8_t **data, size_t *data_size, 57 | uint8_t *optional_data, size_t optional_data_size); 58 | extern ssize_t get_extra_args(uint8_t *data, ssize_t data_size); 59 | 60 | typedef struct { 61 | uint8_t mirror_version; 62 | uint8_t mirror_memory_below_4gb; 63 | uint16_t mirror_amount_above_4gb; 64 | uint8_t mirror_status; 65 | } __attribute__((packed)) ADDRESS_RANGE_MIRROR_VARIABLE_DATA; 66 | 67 | #define MIRROR_VERSION 1 68 | 69 | #define ADDRESS_RANGE_MIRROR_VARIABLE_CURRENT "MirrorCurrent" 70 | #define ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST "MirrorRequest" 71 | -------------------------------------------------------------------------------- /src/efibootdump.8.in: -------------------------------------------------------------------------------- 1 | .TH "EFIBOOTDUMP" "8" "24 February 2016" "" "" 2 | 3 | .SH NAME 4 | efibootdump \- dump a boot entries from a variable or a file 5 | .SH SYNOPSIS 6 | 7 | \fBefibootdump\fR [\fB-?\fR|\fB--help\fR] [\fB--usage\fR] 8 | .br 9 | [\fB-f\fR \fI\fR [... \fB-f\fR \fI\fR]] 10 | .br 11 | [[\fB-g\fR \fI{guid}\fR] \fI\fR [... [\fI\fR]]] 12 | .SH "DESCRIPTION" 13 | .PP 14 | \fBefibootdump\fR is a userspace application used to display individual UEFI boot options, from a file or a UEFI variable. This allows e.g. saved files from efivarfs to be displayed, as well as variables on the running machine. 15 | 16 | .SH "OPTIONS" 17 | The following is a list of options accepted by efibootmgr: 18 | .TP 19 | \fB-g | --guid\fR \fI{guid}\fR 20 | Any variables specified by name have the specified GUID. 21 | .TP 22 | \fB-f | --file\fR \fI\fR 23 | Read a single boot variable from the specified file. 24 | .TP 25 | \fI\fR 26 | Display the specified variable on the local machine. If no GUID is specified, EFI Global Variable is the default. 27 | .SH "BUGS" 28 | .PP 29 | Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr \&. 30 | .SH "SEE ALSO" 31 | .PP 32 | efibootmgr(8) 33 | -------------------------------------------------------------------------------- /src/efibootdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * efibootdump.c - dump a variable as if it's a Boot#### variable 3 | * 4 | * Copyright 2015 Red Hat, Inc. 5 | * 6 | * See "COPYING" for license terms. 7 | * 8 | * Author: Peter Jones 9 | */ 10 | 11 | #include "fix_coverity.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "error.h" 27 | #include "parse_loader_data.h" 28 | 29 | int verbose; 30 | 31 | #define _(String) gettext (String) 32 | #define Q_(String) dgettext (NULL, String) 33 | #define C_(Context,String) dgettext (Context,String) 34 | 35 | static void 36 | print_boot_entry(efi_load_option *loadopt, size_t data_size) 37 | { 38 | char *text_path = NULL; 39 | size_t text_path_len = 0; 40 | uint8_t *optional_data = NULL; 41 | size_t optional_data_len = 0; 42 | uint16_t pathlen; 43 | const unsigned char *desc; 44 | char *raw; 45 | size_t raw_len; 46 | 47 | ssize_t rc; 48 | efidp dp = NULL; 49 | 50 | printf("%c ", (efi_loadopt_attrs(loadopt) & LOAD_OPTION_ACTIVE) 51 | ? '*' : ' '); 52 | 53 | desc = efi_loadopt_desc(loadopt, data_size); 54 | if (!desc) 55 | printf(" "); 56 | else if (desc[0]) 57 | printf("%s ", desc); 58 | 59 | dp = efi_loadopt_path(loadopt, data_size); 60 | pathlen = efi_loadopt_pathlen(loadopt, data_size); 61 | 62 | rc = efidp_format_device_path(NULL, 0, dp, pathlen); 63 | if (rc < 0) { 64 | printf(""); 65 | return; 66 | } 67 | text_path_len = rc + 1; 68 | text_path = alloca(text_path_len); 69 | if (!text_path) 70 | error(100, "Couldn't allocate memory"); 71 | rc = efidp_format_device_path((unsigned char *)text_path, 72 | text_path_len, dp, pathlen); 73 | if (rc < 0) { 74 | printf(""); 75 | return; 76 | } 77 | if (text_path && text_path_len >= 1) 78 | printf("%s", text_path); 79 | 80 | rc = efi_loadopt_optional_data(loadopt, data_size, 81 | &optional_data, &optional_data_len); 82 | if (rc < 0) { 83 | printf(""); 84 | return; 85 | } 86 | 87 | rc = parse_raw_text(NULL, 0, optional_data, optional_data_len); 88 | if (rc < 0) { 89 | printf(""); 90 | return; 91 | } 92 | 93 | raw_len = rc + 1; 94 | raw = alloca(raw_len); 95 | if (!raw) 96 | error(101, "Couldn't allocate memory"); 97 | 98 | rc = parse_raw_text(raw, raw_len, optional_data, optional_data_len); 99 | if (rc < 0) { 100 | printf(""); 101 | } else if (rc > 0) { 102 | for (unsigned int i = 0; i < optional_data_len; i++) 103 | putchar(isprint(optional_data[i]) 104 | ? optional_data[i] 105 | : '.'); 106 | } 107 | 108 | printf("\n"); 109 | } 110 | 111 | int 112 | main(int argc, char *argv[]) 113 | { 114 | const char **names = NULL; 115 | const char **files = NULL; 116 | char *guidstr = NULL; 117 | efi_guid_t guid = efi_guid_global; 118 | 119 | setlocale(LC_ALL, ""); 120 | bindtextdomain("efibootdump", LOCALEDIR); 121 | textdomain("efibootdump"); 122 | 123 | struct poptOption options[] = { 124 | {.argInfo = POPT_ARG_INTL_DOMAIN, 125 | .arg = "efibootdump" }, 126 | {.longName = "guid", 127 | .shortName = 'g', 128 | .argInfo = POPT_ARG_STRING | 129 | POPT_ARGFLAG_OPTIONAL | 130 | POPT_ARGFLAG_STRIP, 131 | .arg = &guidstr, 132 | .descrip = _("GUID namespace the variable is in"), 133 | .argDescrip = "{guid}"}, 134 | {.longName = "file", 135 | .shortName = 'f', 136 | .argInfo = POPT_ARG_ARGV | 137 | POPT_ARGFLAG_OPTIONAL | 138 | POPT_ARGFLAG_STRIP, 139 | .arg = &files, 140 | .descrip = _("File to read variable data from"), 141 | .argDescrip = ""}, 142 | {.longName = "verbose", 143 | .shortName = 'v', 144 | .argInfo = POPT_ARG_VAL | 145 | POPT_ARGFLAG_OPTIONAL | 146 | POPT_ARGFLAG_STRIP, 147 | .arg = &verbose, 148 | .val = 2, 149 | .descrip = _("Be more verbose on errors"), 150 | }, 151 | POPT_AUTOALIAS 152 | POPT_AUTOHELP 153 | POPT_TABLEEND 154 | }; 155 | efi_load_option *loadopt; 156 | uint8_t *data = NULL; 157 | size_t data_size = 0; 158 | 159 | poptContext optcon; 160 | optcon = poptGetContext("efibootdump", argc, (const char **)argv, 161 | options, 0); 162 | 163 | poptSetOtherOptionHelp(optcon, "[OPTIONS...] [name0 [... [nameN]]]"); 164 | 165 | int rc; 166 | rc = poptReadDefaultConfig(optcon, 0); 167 | if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) 168 | errorx(1, _("poptReadDefaultConfig failed: %s: %s"), 169 | poptBadOption(optcon, 0), poptStrerror(rc)); 170 | 171 | while ((rc = poptGetNextOpt(optcon)) > 0) 172 | ; 173 | 174 | if (rc < -1) 175 | errorx(2, "Invalid argument: \"%s\": %s", 176 | poptBadOption(optcon, 0), poptStrerror(rc)); 177 | 178 | /* argc = */ poptStrippedArgv(optcon, argc, argv); 179 | names = poptGetArgs(optcon); 180 | if (!names && !files) { 181 | poptPrintUsage(optcon, stderr, 0); 182 | exit(4); 183 | } 184 | 185 | if (names && (!names[0] || names[0][0] == '\0')) { 186 | poptPrintUsage(optcon, stderr, 0); 187 | exit(4); 188 | } 189 | 190 | if (files && (!files[0] || files[0][0] == '\0')) { 191 | poptPrintUsage(optcon, stderr, 0); 192 | exit(4); 193 | } 194 | 195 | if (names) { 196 | if (guidstr) { 197 | rc = efi_id_guid_to_guid(guidstr, &guid); 198 | if (rc < 0) 199 | error(5, "Could not parse guid \"%s\"", 200 | guidstr); 201 | } 202 | free(guidstr); 203 | guidstr = NULL; 204 | rc = efi_guid_to_str(&guid, &guidstr); 205 | if (rc < 0) 206 | error(6, "Guid lookup failed"); 207 | } 208 | 209 | for (unsigned int i = 0; 210 | files != NULL && files[i] != NULL && files[i][0] != '\0'; 211 | i++) { 212 | struct stat statbuf; 213 | FILE *f; 214 | size_t n; 215 | const char *filename = files[i]; 216 | 217 | memset(&statbuf, 0, sizeof(statbuf)); 218 | rc = stat(filename, &statbuf); 219 | if (rc < 0) 220 | error(7, "Could not stat \"%s\"", filename); 221 | 222 | data_size = statbuf.st_size; 223 | if (data_size == 0) 224 | errorx(11, "File \"%s\" is empty", filename); 225 | 226 | data = alloca(data_size); 227 | if (data == NULL) 228 | error(8, "Could not allocate memory"); 229 | 230 | f = fopen(filename, "r"); 231 | if (!f) 232 | error(9, "Could not open \"%s\"", filename); 233 | 234 | n = fread(data, 1, data_size, f); 235 | if (n < data_size) 236 | error(10, "Could not read \"%s\"", filename); 237 | 238 | printf("%s: ", filename); 239 | loadopt = (efi_load_option *)(data + 4); 240 | if (data_size <= 8) 241 | errorx(11, "Data is not a valid load option"); 242 | if (efi_loadopt_is_valid(loadopt, data_size - 4)) { 243 | print_boot_entry(loadopt, data_size - 4); 244 | } else { 245 | loadopt = (efi_load_option *)data; 246 | if (!efi_loadopt_is_valid(loadopt, data_size)) 247 | errorx(11, "Data is not a valid load option"); 248 | print_boot_entry(loadopt, data_size); 249 | } 250 | 251 | fclose(f); 252 | } 253 | 254 | for (unsigned int i = 0; 255 | names && names[i] != NULL && names[i][0] != '\0'; 256 | i++) { 257 | uint32_t attrs = 0; 258 | 259 | rc = efi_get_variable(guid, names[i], &data, &data_size, 260 | &attrs); 261 | if (rc < 0) { 262 | warning("couldn't read variable %s-%s", 263 | names[i], guidstr); 264 | continue; 265 | } 266 | 267 | loadopt = (efi_load_option *)data; 268 | if (!efi_loadopt_is_valid(loadopt, data_size)) { 269 | warning("load option for %s is not valid", names[i]); 270 | printf("%d\n", __LINE__); 271 | if (data && data_size > 0) { 272 | free(data); 273 | continue; 274 | } 275 | } 276 | 277 | printf("%s", names[i]); 278 | if (efi_guid_cmp(&efi_guid_global, &guid)) 279 | printf("-%s", guidstr); 280 | printf(": "); 281 | print_boot_entry(loadopt, data_size); 282 | if (data && data_size > 0) 283 | free(data); 284 | } 285 | 286 | if (guidstr) 287 | free(guidstr); 288 | 289 | poptFreeContext(optcon); 290 | return 0; 291 | } 292 | -------------------------------------------------------------------------------- /src/efibootmgr.8.in: -------------------------------------------------------------------------------- 1 | .TH "EFIBOOTMGR" "8" "28 September 2021" "" "" 2 | 3 | .SH NAME 4 | efibootmgr \- change the UEFI Boot Manager configuration 5 | .SH SYNOPSIS 6 | 7 | \fBefibootmgr\fR [ \fB-a\fR ] [ \fB-A\fR ] [ \fB-b \fIXXXX\fB\fR ] [ \fB-B\fR ] [ \fB-c\fR ] [ \fB-d \fIDISK\fB\fR ] [ \fB-D\fR ] [ \fB-e \fI1|3|-1\fB\fR ] [ \fB-E \fINUM\fB\fR ] [ \fB--full-dev-path\fR | \fB--file-dev-path\fR ] [ \fB-f\fR ] [ \fB-F\fR ] [ \fB-g\fR ] [ \fB-i \fINAME\fB\fR ] [ \fB-l \fINAME\fB\fR ] [ \fB-L \fILABEL\fB\fR ] [ \fB-m \fIt|f\fB\fR ] [ \fB-M \fIX\fB\fR ] [ \fB-n \fIXXXX\fB\fR ] [ \fB-N\fR ] [ \fB-o \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR\fI ...\fR ] [ \fB-O\fR ] [ \fB-p \fIPART\fB\fR ] [ \fB-q\fR ] [ \fB-r\fR | \fB-y\fR ] [ \fB-s\fR ] [ \fB-t \fIseconds\fB\fR ] [ \fB-T\fR ] [ \fB-u\fR ] [ \fB-v\fR ] [ \fB-V\fR ] [ \fB-@ \fIfile\fB\fR ] 8 | 9 | .SH "DESCRIPTION" 10 | .PP 11 | \fBefibootmgr\fR is a userspace application used to modify the UEFI Boot 12 | Manager. This application can create and destroy boot entries, change the boot 13 | order, change the next running boot option, and more. 14 | .PP 15 | Details on the UEFI Boot Manager are available from the UEFI 16 | Specification, v1.02 or later, available from: http://www.uefi.org 17 | .sp 18 | .RS 19 | .B "Note:" 20 | efibootmgr requires that the kernel support access to EFI 21 | non-volatile variables through 22 | \fI/sys/firmware/efi/vars\fR or \fI/sys/firmware/efi/efivars/\fR. 23 | .RE 24 | .SH "OPTIONS" 25 | .PP 26 | The following is a list of options accepted by efibootmgr: 27 | .TP 28 | \fB-a | --active\fR 29 | Set bootnum active. 30 | .TP 31 | \fB-A | --inactive\fR 32 | Set bootnum inactive. 33 | .TP 34 | \fB-b | --bootnum \fIXXXX\fB\fR 35 | Modify Boot\fIXXXX\fR (hex). 36 | .TP 37 | \fB-B | --delete-bootnum\fR 38 | Delete bootnum. 39 | .TP 40 | \fB-c | --create\fR 41 | Create new variable bootnum and add to bootorder. 42 | .TP 43 | \fB-C | --create-only\fR 44 | Create new variable bootnum and and do not add to bootorder. 45 | .TP 46 | \fB-d | --disk \fIDISK\fB\fR 47 | The disk containing the loader (defaults to 48 | \fI/dev/sda\fR). 49 | .TP 50 | \fB-D | --remove-dups\fR 51 | Remove duplicated entries from BootOrder. 52 | .TP 53 | \fB-e | --edd \fI1|3\fB\fR 54 | Force EDD 1.0 or 3.0 creation variables. 55 | 56 | Use \fB-e 1\fR together with \fB-E\fR if you are using a very old system which 57 | uses UEFI to boot, but requires legacy CSM (BIOS) device drivers for storage 58 | devices. 59 | 60 | \fB-e 3\fR is now deprecated, and is an alias for \fB--full-dev-path\fR. 61 | .TP 62 | \fB-E | --edd-device \fINUM\fB\fR 63 | EDD 1.0 device number (defaults to 0x80). See \fB--edd\fR. 64 | .TP 65 | \fB--full-dev-path\fR 66 | Force creation of boot entries use a full UEFI device path, starting at the 67 | PCIe root or equivalent on the current platform. The default is to use a hard 68 | disk based HD() abbreviated path. 69 | 70 | You shouldn't need to use this option unless the system firmware won't boot off 71 | of your device using an abbreviated HD() device path. 72 | 73 | Note that forcing a full path will fail if we don't know what the system device 74 | root is, how the specified device is connected to it, or how to encode any one 75 | of those. 76 | .TP 77 | \fB--file-dev-path\fR 78 | Force creation of boot entries use an abbreviated UEFI device path which starts 79 | with the File() portion of the path. The default is to use a hard disk based 80 | HD() abbreviated path. 81 | .TP 82 | \fB-f | --reconnect \fR 83 | Re-connect devices after driver is loaded. Only applicable for driver entries. 84 | .TP 85 | \fB-F | --do-not-reconnect \fR 86 | Do not reconnect devices after driver is loaded. Only applicable for driver entries. 87 | .TP 88 | \fB-g | --gpt\fR 89 | Force disk with invalid PMBR to be treated as GPT. 90 | .TP 91 | \fB-i | --iface \fINAME\fB\fR 92 | Create a netboot entry for the named interface. 93 | .TP 94 | \fB-I | --index \fIINDEX\fB\fR 95 | When creating a new entry, insert at the position (0-indexed, defaults to 0). 96 | .TP 97 | \fB-k | --keep \fINAME\fB\fR 98 | Keep old entries when adjusting order. 99 | .TP 100 | \fB-l | --loader \fINAME\fB\fR 101 | Specify a loader (defaults to \fI@@DEFAULT_LOADER@@\fR). 102 | .TP 103 | \fB-L | --label \fILABEL\fB\fR 104 | Boot manager display label (defaults to "Linux"). 105 | .TP 106 | \fB-m | --mirror-below-4G \fIt|f\fB\fR 107 | Set t if you want to mirror memory below 4GB. 108 | .TP 109 | \fB-M | --mirror-above-4G \fIX\fB\fR 110 | X percentage memory to mirror above 4GB. Floating-point value with up to 2 decimal places is accepted. 111 | .TP 112 | \fB-n | --bootnext \fIXXXX\fB\fR 113 | Set BootNext to XXXX (hex). 114 | .TP 115 | \fB-N | --delete-bootnext\fR 116 | Delete BootNext. 117 | .TP 118 | \fB-o | --bootorder \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR 119 | Explicitly set BootOrder (hex). Any value from 0 to FFFF is accepted so long as it corresponds to an existing Boot#### variable, and zero padding is not required. 120 | .TP 121 | \fB-O | --delete-bootorder\fR 122 | Delete BootOrder. 123 | .TP 124 | \fB-p | --part \fIPART\fB\fR 125 | Partition number containing the bootloader (defaults to 1). 126 | .TP 127 | \fB-q | --quiet\fR 128 | Quiet mode - suppresses output. 129 | .TP 130 | \fB-r | --driver\fR 131 | Operate on Driver#### variables instead of Boot#### variables. 132 | .TP 133 | \fB-s | --list-signature-types\fR 134 | List cryptographic signature types supported on this machine and exit. If in 135 | verbose mode, the symbolic name as well as the raw GUID will be displayed. 136 | Consult the UEFI Specification for more details. 137 | .TP 138 | \fB-t | --timeout \fIseconds\fB\fR 139 | Boot Manager timeout, in \fIseconds\fR\&. 140 | .TP 141 | \fB-T | --delete-timeout\fR 142 | Delete Timeout variable. 143 | .TP 144 | \fB-u | --unicode | --UCS-2 \fR 145 | Handle extra command line arguments as UCS-2 (default is 146 | ASCII). 147 | .TP 148 | \fB-v | --verbose\fR 149 | Verbose mode - prints additional information. 150 | .TP 151 | \fB-V | --version\fR 152 | Just print version string and exit. 153 | .TP 154 | \fB-y | --sysprep\fR 155 | Operate on SysPrep#### variables instead of Boot#### variables. 156 | .TP 157 | \fB-@ | --append-binary-args \fR 158 | Append extra variable args from file (use - to read 159 | from stdin). Data in file is appended as command line 160 | arguments to the boot loader command, with no modification to 161 | the data, so you can pass any binary or text data necessary. 162 | .SH "EXAMPLES" 163 | \fR 164 | .SS "Displaying the current settings (must be root):" 165 | \fR 166 | .nf 167 | .B 168 | [root@localhost ~]# efibootmgr 169 | BootCurrent: 0004 170 | BootNext: 0003 171 | BootOrder: 0004,0000,0001,0002,0003 172 | Timeout: 30 seconds 173 | Boot0000* Diskette Drive(device:0) 174 | Boot0001* CD-ROM Drive(device:FF) 175 | Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) 176 | Boot0003* PXE Boot: MAC(00D0B7C15D91) 177 | Boot0004* Linux 178 | .fi 179 | .PP 180 | Each of the above are boot variables, which are defined as follows: 181 | .RS 182 | .TP 0.2i 183 | \(bu 184 | BootCurrent - the boot entry used to start the currently 185 | running system 186 | .TP 0.2i 187 | \(bu 188 | BootOrder - the boot order as would appear in the boot manager. 189 | The boot manager tries to boot the first active entry in this 190 | list. If unsuccessful, it tries the next entry, and so on. 191 | .TP 0.2i 192 | \(bu 193 | BootNext - the boot entry which is scheduled to be run on next 194 | boot. This supersedes BootOrder for one boot only, and is 195 | deleted by the boot manager after first use. This allows you 196 | to change the next boot behavior without changing BootOrder. 197 | .TP 0.2i 198 | \(bu 199 | Timeout - the time in seconds between when the boot 200 | manager appears on the screen until when it 201 | automatically chooses the startup value from BootNext 202 | or BootOrder. 203 | .TP 0.2i 204 | \(bu 205 | Five boot entries (0000 - 0004), along with the active/inactive 206 | flag (* means active) and the name displayed on the screen. 207 | .RE 208 | .SS "Creating a new boot option" 209 | An OS installer would call \fBefibootmgr -c\fR\&. 210 | This assumes that \fI/dev/sda1\fR is your EFI System 211 | Partition, and is mounted at \fI/boot/efi\fR\&. This 212 | creates a new boot option, called "Linux", and puts it at the top of 213 | the boot order list. Options may be passed to modify the default 214 | behavior. The default OS Loader is \fI@@DEFAULT_LOADER@@\fR\&. 215 | .SS "Changing the boot order" 216 | Assuming the configuration in the first example, 217 | \fBefibootmgr -o 3,4\fR could be called to specify 218 | PXE boot first, then Linux boot. 219 | .SS "Changing the boot order for the next boot only" 220 | Assuming the configuration in the first example, 221 | \fBefibootmgr -n 4\fR could be called to specify 222 | that the Linux entry be taken on next boot. 223 | .SS "Deleting a boot option" 224 | Assuming the configuration in the first example, 225 | \fBefibootmgr -b 4 -B\fR could be called to delete 226 | entry 4 and remove it from the BootOrder. 227 | .SS "Creating network boot entries" 228 | A system administrator wants to create a boot option to network 229 | boot. You create the boot entry with: 230 | \fBefibootmgr -c -i eth0 -L netboot [ -l '\\filename.efi' ]\fR 231 | .SS "Displaying the supported cryptographic signature types" 232 | \fR 233 | .nf 234 | .B 235 | [root@localhost ~]# efibootmgr -s 236 | x509_sha256 237 | x509_sha384 238 | x509_sha512 239 | sha256 240 | x509_cert 241 | rsa2048 242 | rsa2048_sha256 243 | rsa2048_sha1 244 | external_management 245 | .B 246 | [root@localhost ~]# efibootmgr -s -v 247 | x509_sha256 3bd2a492-96c0-4079-b420-fcf98ef103ed 248 | x509_sha384 7076876e-80c2-4ee6-aad2-28b349a6865b 249 | x509_sha512 446dbf63-2502-4cda-bcfa-2465d2b0fe9d 250 | sha256 c1c41626-504c-4092-aca9-41f936934328 251 | x509_cert a5c059a1-94e4-4aa7-87b5-ab155c2bf072 252 | rsa2048 3c5766e8-269c-4e34-aa14-ed776e85b3b6 253 | rsa2048_sha256 e2b36190-879b-4a3d-ad8d-f2e7bba32784 254 | rsa2048_sha1 67f8444f-8743-48f1-a328-1eaab8736080 255 | external_management 452e8ced-dfff-4b8c-ae01-5118862e682c 256 | .fi 257 | .SH "BUGS" 258 | .PP 259 | Please direct any bugs, features, patches, etc. to the Red Hat bootloader team at https://github.com/rhboot/efibootmgr \&. 260 | .SH "AUTHOR" 261 | .PP 262 | This man page was generated by dann frazier for the 263 | Debian GNU/Linux operating system and updated by Robert Bisewski 264 | , but may be used by others. 265 | .PP 266 | It has subsequently been modified by Robbie Harwood for 267 | the efibootmgr project. 268 | -------------------------------------------------------------------------------- /src/efibootmgr.c: -------------------------------------------------------------------------------- 1 | /* 2 | efibootmgr.c - Manipulates EFI variables as exported in /sys/firmware/efi/ 3 | efivars or vars (previously /proc/efi/vars) 4 | 5 | Copyright (C) 2001-2004 Dell, Inc. 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | 22 | This must tie the EFI_DEVICE_PATH to /boot/efi/EFI//.efi 23 | The EFI_DEVICE_PATH will look something like: 24 | ACPI device path, length 12 bytes 25 | Hardware Device Path, PCI, length 6 bytes 26 | Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ?? 27 | Media Device Path, Hard Drive, partition XX, length 30 bytes 28 | Media Device Path, File Path, length ?? 29 | End of Hardware Device Path, length 4 30 | Arguments passed to elilo, as UCS-2 characters, length ?? 31 | 32 | */ 33 | 34 | #include "fix_coverity.h" 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "list.h" 55 | #include "efi.h" 56 | #include "parse_loader_data.h" 57 | #include "efibootmgr.h" 58 | #include "error.h" 59 | 60 | #ifndef EFIBOOTMGR_VERSION 61 | #define EFIBOOTMGR_VERSION "unknown (fix Makefile!)" 62 | #endif 63 | 64 | int verbose; 65 | 66 | typedef struct _var_entry { 67 | char *name; 68 | efi_guid_t guid; 69 | uint8_t *data; 70 | size_t data_size; 71 | uint32_t attributes; 72 | uint16_t num; 73 | list_t list; 74 | } var_entry_t; 75 | 76 | /* global variables */ 77 | static LIST_HEAD(entry_list); 78 | static LIST_HEAD(blk_list); 79 | efibootmgr_opt_t opts; 80 | 81 | static void 82 | free_vars(list_t *head) 83 | { 84 | list_t *pos, *n; 85 | var_entry_t *entry; 86 | 87 | list_for_each_safe(pos, n, head) { 88 | entry = list_entry(pos, var_entry_t, list); 89 | if (entry->name) 90 | free(entry->name); 91 | if (entry->data) 92 | free(entry->data); 93 | list_del(&(entry->list)); 94 | free(entry); 95 | } 96 | } 97 | 98 | static void 99 | read_vars(char **namelist, 100 | list_t *head) 101 | { 102 | var_entry_t *entry; 103 | int i, rc; 104 | 105 | if (!namelist) 106 | return; 107 | 108 | for (i=0; namelist[i] != NULL; i++) { 109 | if (namelist[i]) { 110 | entry = calloc(1, sizeof(var_entry_t)); 111 | if (!entry) { 112 | efi_error("calloc(1, %zd) failed", 113 | sizeof(var_entry_t)); 114 | goto err; 115 | } 116 | 117 | rc = efi_get_variable(EFI_GLOBAL_GUID, namelist[i], 118 | &entry->data, &entry->data_size, 119 | &entry->attributes); 120 | if (rc < 0) { 121 | warning("Skipping unreadable variable \"%s\"", 122 | namelist[i]); 123 | free(entry); 124 | continue; 125 | } 126 | 127 | /* latest apple firmware sets high bit which appears 128 | * invalid to the linux kernel if we write it back so 129 | * lets zero it out if it is set since it would be 130 | * invalid to set it anyway */ 131 | entry->attributes = entry->attributes & ~(1 << 31); 132 | 133 | entry->name = strdup(namelist[i]); 134 | if (!entry->name) { 135 | efi_error("strdup(\"%s\") failed", namelist[i]); 136 | goto err; 137 | } 138 | entry->guid = EFI_GLOBAL_GUID; 139 | list_add_tail(&entry->list, head); 140 | } 141 | } 142 | return; 143 | err: 144 | exit(1); 145 | } 146 | 147 | static void 148 | free_array(char **array) 149 | { 150 | int i; 151 | 152 | if (!array) 153 | return; 154 | 155 | for (i = 0; array[i] != NULL; i++) 156 | free(array[i]); 157 | 158 | free(array); 159 | } 160 | 161 | static int 162 | compare(const void *a, const void *b) 163 | { 164 | int rc = -1; 165 | uint16_t n1, n2; 166 | memcpy(&n1, a, sizeof(n1)); 167 | memcpy(&n2, b, sizeof(n2)); 168 | if (n1 < n2) rc = -1; 169 | if (n1 == n2) rc = 0; 170 | if (n1 > n2) rc = 1; 171 | return rc; 172 | } 173 | 174 | 175 | /* 176 | Return an available variable number, 177 | or -1 on failure. 178 | */ 179 | static int 180 | find_free_var(list_t *var_list) 181 | { 182 | int num_vars=0, i=0, found; 183 | uint16_t *vars, free_number; 184 | list_t *pos; 185 | var_entry_t *entry; 186 | 187 | list_for_each(pos, var_list) 188 | num_vars++; 189 | 190 | if (num_vars == 0) 191 | return 0; 192 | 193 | vars = calloc(1, sizeof(uint16_t) * num_vars); 194 | if (!vars) { 195 | efi_error("calloc(1, %zd) failed", sizeof(uint16_t) * num_vars); 196 | return -1; 197 | } 198 | 199 | list_for_each(pos, var_list) { 200 | entry = list_entry(pos, var_entry_t, list); 201 | vars[i] = entry->num; 202 | i++; 203 | } 204 | qsort(vars, i, sizeof(uint16_t), compare); 205 | found = 1; 206 | 207 | num_vars = i; 208 | for (free_number = 0; free_number < num_vars && found; free_number++) { 209 | found = 0; 210 | list_for_each(pos, var_list) { 211 | entry = list_entry(pos, var_entry_t, list); 212 | if (entry->num == free_number) { 213 | found = 1; 214 | break; 215 | } 216 | } 217 | if (!found) 218 | break; 219 | } 220 | if (found && num_vars) 221 | free_number = vars[num_vars-1] + 1; 222 | free(vars); 223 | return free_number; 224 | } 225 | 226 | 227 | static void 228 | warn_duplicate_name(list_t *var_list) 229 | { 230 | list_t *pos; 231 | var_entry_t *entry; 232 | efi_load_option *load_option; 233 | const unsigned char *desc; 234 | 235 | list_for_each(pos, var_list) { 236 | entry = list_entry(pos, var_entry_t, list); 237 | load_option = (efi_load_option *)entry->data; 238 | desc = efi_loadopt_desc(load_option, entry->data_size); 239 | if (!strcmp((char *)opts.label, (char *)desc)) 240 | warnx("** Warning ** : %s has same label %s", 241 | entry->name, opts.label); 242 | } 243 | } 244 | 245 | static var_entry_t * 246 | make_var(const char *prefix, list_t *var_list) 247 | { 248 | var_entry_t *entry = NULL; 249 | int free_number; 250 | list_t *pos; 251 | int rc; 252 | uint8_t *extra_args = NULL; 253 | ssize_t extra_args_size = 0; 254 | ssize_t needed=0, sz; 255 | 256 | if (opts.num == -1) { 257 | free_number = find_free_var(var_list); 258 | } else { 259 | list_for_each(pos, var_list) { 260 | entry = list_entry(pos, var_entry_t, list); 261 | if (entry->num == opts.num) 262 | errx(40, 263 | "Cannot create %s%04X: already exists.", 264 | prefix, opts.num); 265 | } 266 | free_number = opts.num; 267 | } 268 | 269 | if (free_number == -1) { 270 | efi_error("efibootmgr: no available %s variables", prefix); 271 | return NULL; 272 | } 273 | 274 | /* Create a new var_entry_t object 275 | and populate it. 276 | */ 277 | entry = calloc(1, sizeof(*entry)); 278 | if (!entry) { 279 | efi_error("calloc(1, %zd) failed", sizeof(*entry)); 280 | return NULL; 281 | } 282 | 283 | sz = get_extra_args(NULL, 0); 284 | if (sz < 0) { 285 | efi_error("get_extra_args() failed"); 286 | goto err; 287 | } 288 | extra_args_size = sz; 289 | 290 | entry->data = NULL; 291 | entry->data_size = 0; 292 | needed = make_linux_load_option(&entry->data, &entry->data_size, 293 | NULL, sz); 294 | if (needed < 0) { 295 | efi_error("make_linux_load_option() failed"); 296 | goto err; 297 | } 298 | entry->data_size = needed; 299 | entry->data = malloc(needed); 300 | if (!entry->data) { 301 | efi_error("malloc(%zd) failed", needed); 302 | goto err; 303 | } 304 | 305 | extra_args = entry->data + needed - extra_args_size; 306 | sz = get_extra_args(extra_args, extra_args_size); 307 | if (sz < 0) { 308 | efi_error("get_extra_args() failed"); 309 | goto err; 310 | } 311 | sz = make_linux_load_option(&entry->data, &entry->data_size, 312 | extra_args, extra_args_size); 313 | if (sz < 0) { 314 | efi_error("make_linux_load_option failed"); 315 | goto err; 316 | } 317 | 318 | entry->num = free_number; 319 | entry->guid = EFI_GLOBAL_GUID; 320 | rc = asprintf(&entry->name, "%s%04X", prefix, free_number); 321 | if (rc < 0) { 322 | efi_error("asprintf failed"); 323 | goto err; 324 | } 325 | entry->attributes = EFI_VARIABLE_NON_VOLATILE | 326 | EFI_VARIABLE_BOOTSERVICE_ACCESS | 327 | EFI_VARIABLE_RUNTIME_ACCESS; 328 | rc = efi_set_variable(entry->guid, entry->name, entry->data, 329 | entry->data_size, entry->attributes, 0644); 330 | if (rc < 0) { 331 | efi_error("efi_set_variable failed"); 332 | goto err; 333 | } 334 | list_add_tail(&entry->list, var_list); 335 | return entry; 336 | err: 337 | if (entry) { 338 | if (entry->data) 339 | free(entry->data); 340 | if (entry->name) { 341 | efi_error("Could not set variable %s", entry->name); 342 | free(entry->name); 343 | } else { 344 | efi_error("Could not set variable"); 345 | } 346 | free(entry); 347 | } 348 | return NULL; 349 | } 350 | 351 | static int 352 | read_order(const char *name, var_entry_t **order) 353 | { 354 | int rc; 355 | var_entry_t *new = NULL, *bo = NULL; 356 | 357 | if (*order == NULL) { 358 | new = calloc(1, sizeof (**order)); 359 | if (!new) { 360 | efi_error("calloc(1, %zd) failed", 361 | sizeof (**order)); 362 | return -1; 363 | } 364 | *order = bo = new; 365 | } else { 366 | bo = *order; 367 | } 368 | 369 | rc = efi_get_variable(EFI_GLOBAL_GUID, name, 370 | &bo->data, &bo->data_size, &bo->attributes); 371 | if (rc < 0 && new != NULL) { 372 | efi_error("efi_get_variable failed"); 373 | free(new); 374 | *order = NULL; 375 | bo = NULL; 376 | } 377 | 378 | if (bo) { 379 | /* latest apple firmware sets high bit which appears invalid 380 | * to the linux kernel if we write it back so lets zero it out 381 | * if it is set since it would be invalid to set it anyway */ 382 | bo->attributes = bo->attributes & ~(1 << 31); 383 | } 384 | return rc; 385 | } 386 | 387 | static int 388 | set_u16(const char *name, uint16_t num) 389 | { 390 | return efi_set_variable(EFI_GLOBAL_GUID, name, (uint8_t *)&num, 391 | sizeof (num), EFI_VARIABLE_NON_VOLATILE | 392 | EFI_VARIABLE_BOOTSERVICE_ACCESS | 393 | EFI_VARIABLE_RUNTIME_ACCESS, 394 | 0644); 395 | } 396 | 397 | static int 398 | add_to_order(const char *name, uint16_t num, uint16_t insert_at) 399 | { 400 | var_entry_t *order = NULL; 401 | uint64_t new_data_size; 402 | uint16_t *new_data, *old_data; 403 | int rc; 404 | 405 | rc = read_order(name, &order); 406 | if (rc < 0) { 407 | if (errno == ENOENT) 408 | rc = set_u16(name, num); 409 | return rc; 410 | } 411 | 412 | /* We've now got an array (in order->data) of the order. Copy over 413 | * any entries that should precede, add our entry, and then copy the 414 | * rest of the old array. 415 | */ 416 | old_data = (uint16_t *)order->data; 417 | new_data_size = order->data_size + sizeof(uint16_t); 418 | new_data = malloc(new_data_size); 419 | if (!new_data) 420 | return -1; 421 | 422 | if (insert_at != 0) { 423 | if (insert_at * sizeof(uint16_t) > order->data_size) 424 | insert_at = order->data_size / sizeof(uint16_t); 425 | memcpy(new_data, old_data, insert_at * sizeof(uint16_t)); 426 | } 427 | new_data[insert_at] = num; 428 | if (order->data_size - insert_at * sizeof(uint16_t) > 0) { 429 | memcpy(new_data + insert_at + 1, old_data + insert_at, 430 | order->data_size - insert_at * sizeof(uint16_t)); 431 | } 432 | 433 | /* Now new_data has what we need */ 434 | free(order->data); 435 | order->data = (uint8_t *)new_data; 436 | order->data_size = new_data_size; 437 | 438 | rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, 439 | order->data_size, order->attributes, 0644); 440 | free(order->data); 441 | free(order); 442 | return rc; 443 | } 444 | 445 | static int 446 | remove_dupes_from_order(char *name) 447 | { 448 | var_entry_t *order = NULL; 449 | uint64_t new_data_size; 450 | uint16_t *new_data, *old_data; 451 | unsigned int old_i,new_i; 452 | int rc; 453 | 454 | rc = read_order(name, &order); 455 | if (rc < 0) { 456 | if (errno == ENOENT) 457 | rc = 0; 458 | return rc; 459 | } 460 | 461 | old_data = (uint16_t *)(order->data); 462 | /* Start with the same size */ 463 | new_data_size = order->data_size; 464 | new_data = malloc(new_data_size); 465 | if (!new_data) 466 | return -1; 467 | 468 | unsigned int old_max = order->data_size / sizeof(*new_data); 469 | for (old_i = 0, new_i = 0; old_i < old_max; old_i++) { 470 | int copies = 0; 471 | unsigned int j; 472 | for (j = 0; j < new_i; j++) { 473 | if (new_data[j] == old_data[old_i]) { 474 | copies++; 475 | break; 476 | } 477 | } 478 | if (copies == 0) { 479 | /* Copy this value */ 480 | new_data[new_i] = old_data[old_i]; 481 | new_i++; 482 | } 483 | } 484 | /* Adjust the size if we didn't copy everything. */ 485 | new_data_size = sizeof(new_data[0]) * new_i; 486 | 487 | /* Now new_data has what we need */ 488 | free(order->data); 489 | order->data = (uint8_t *)new_data; 490 | order->data_size = new_data_size; 491 | efi_del_variable(EFI_GLOBAL_GUID, name); 492 | rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, 493 | order->data_size, order->attributes, 494 | 0644); 495 | free(order->data); 496 | free(order); 497 | return rc; 498 | } 499 | 500 | static int 501 | remove_from_order(const char *name, uint16_t num) 502 | { 503 | var_entry_t *order = NULL; 504 | uint16_t *data; 505 | unsigned int old_i,new_i; 506 | int rc; 507 | 508 | rc = read_order(name, &order); 509 | if (rc < 0) { 510 | if (errno == ENOENT) 511 | rc = 0; 512 | return rc; 513 | } 514 | 515 | /* We've now got an array (in order->data) of the 516 | order. Squeeze out any instance of the entry we're 517 | deleting by shifting the remainder down. 518 | */ 519 | data = (uint16_t *)(order->data); 520 | 521 | for (old_i=0,new_i=0; 522 | old_i < order->data_size / sizeof(data[0]); 523 | old_i++) { 524 | if (data[old_i] != num) { 525 | if (new_i != old_i) 526 | data[new_i] = data[old_i]; 527 | new_i++; 528 | } 529 | } 530 | 531 | /* If nothing removed, no need to update the order variable */ 532 | if (new_i == old_i) 533 | goto all_done; 534 | 535 | /* *Order should have nothing when new_i == 0 */ 536 | if (new_i == 0) { 537 | efi_del_variable(EFI_GLOBAL_GUID, name); 538 | goto all_done; 539 | } 540 | 541 | order->data_size = sizeof(data[0]) * new_i; 542 | rc = efi_set_variable(EFI_GLOBAL_GUID, name, order->data, 543 | order->data_size, order->attributes, 544 | 0644); 545 | all_done: 546 | free(order->data); 547 | free(order); 548 | return rc; 549 | } 550 | 551 | static int 552 | read_u16(const char *name) 553 | { 554 | efi_guid_t guid = EFI_GLOBAL_GUID; 555 | uint16_t *data = NULL; 556 | size_t data_size = 0; 557 | uint32_t attributes = 0; 558 | int rc; 559 | 560 | rc = efi_get_variable(guid, name, (uint8_t **)&data, &data_size, 561 | &attributes); 562 | if (rc < 0) 563 | return rc; 564 | if (data_size != 2) { 565 | if (data != NULL) 566 | free(data); 567 | errno = EINVAL; 568 | return -1; 569 | } 570 | 571 | rc = data[0]; 572 | free(data); 573 | return rc; 574 | } 575 | 576 | static int 577 | hex_could_be_lower_case(uint16_t num) 578 | { 579 | return ((((num & 0x000f) >> 0) > 9) || 580 | (((num & 0x00f0) >> 4) > 9) || 581 | (((num & 0x0f00) >> 8) > 9) || 582 | (((num & 0xf000) >> 12) > 9)); 583 | } 584 | 585 | static int 586 | delete_var(const char *prefix, uint16_t num) 587 | { 588 | int rc; 589 | char name[16]; 590 | list_t *pos, *n; 591 | var_entry_t *entry; 592 | 593 | snprintf(name, sizeof(name), "%s%04X", prefix, num); 594 | rc = efi_del_variable(EFI_GLOBAL_GUID, name); 595 | if (rc < 0) 596 | efi_error("Could not delete %s%04X", prefix, num); 597 | 598 | /* For backwards compatibility, try to delete abcdef entries as well */ 599 | if (rc < 0 && errno == ENOENT && hex_could_be_lower_case(num)) { 600 | snprintf(name, sizeof(name), "%s%04x", prefix, num); 601 | rc = efi_del_variable(EFI_GLOBAL_GUID, name); 602 | if (rc < 0 && errno != ENOENT) 603 | efi_error("Could not delete %s%04x", prefix, num); 604 | } 605 | 606 | if (rc < 0) 607 | return rc; 608 | else 609 | efi_error_clear(); 610 | 611 | snprintf(name, sizeof(name), "%sOrder", prefix); 612 | 613 | list_for_each_safe(pos, n, &entry_list) { 614 | entry = list_entry(pos, var_entry_t, list); 615 | if (entry->num == num) { 616 | rc = remove_from_order(name, num); 617 | if (rc < 0) { 618 | efi_error("remove_from_order(%s,%d) failed", 619 | name, num); 620 | return rc; 621 | } 622 | list_del(&(entry->list)); 623 | free(entry->name); 624 | free(entry->data); 625 | memset(entry, 0, sizeof(*entry)); 626 | free(entry); 627 | break; /* short-circuit since it was found */ 628 | } 629 | } 630 | return 0; 631 | } 632 | 633 | static int 634 | delete_label(const char *prefix, const unsigned char *label) 635 | { 636 | list_t *pos; 637 | var_entry_t *boot; 638 | int num_deleted = 0; 639 | int rc; 640 | efi_load_option *load_option; 641 | const unsigned char *desc; 642 | 643 | list_for_each(pos, &entry_list) { 644 | boot = list_entry(pos, var_entry_t, list); 645 | load_option = (efi_load_option *)boot->data; 646 | desc = efi_loadopt_desc(load_option, boot->data_size); 647 | 648 | if (strcmp((char *)desc, (char *)label) == 0) { 649 | rc = delete_var(prefix,boot->num); 650 | if (rc < 0) { 651 | efi_error("Could not delete %s%04x", prefix, boot->num); 652 | return rc; 653 | } else { 654 | num_deleted++; 655 | } 656 | } 657 | } 658 | 659 | if (num_deleted == 0) { 660 | efi_error("Could not delete %s", label); 661 | return -1; 662 | } 663 | 664 | return 0; 665 | } 666 | 667 | static void 668 | set_var_nums(const char *prefix, list_t *list) 669 | { 670 | list_t *pos; 671 | var_entry_t *var; 672 | int num=0, rc; 673 | char *name; 674 | int warn=0; 675 | size_t plen = strlen(prefix); 676 | char fmt[30]; 677 | 678 | fmt[0] = '\0'; 679 | strcat(fmt, prefix); 680 | strcat(fmt, "%04X-%*s"); 681 | 682 | list_for_each(pos, list) { 683 | var = list_entry(pos, var_entry_t, list); 684 | rc = sscanf(var->name, fmt, &num); 685 | if (rc == 1) { 686 | char *snum; 687 | var->num = num; 688 | name = var->name; /* shorter name */ 689 | snum = name + plen; 690 | if ((isalpha(snum[0]) && islower(snum[0])) || 691 | (isalpha(snum[1]) && islower(snum[1])) || 692 | (isalpha(snum[2]) && islower(snum[2])) || 693 | (isalpha(snum[3]) && islower(snum[3]))) { 694 | fprintf(stderr, 695 | "** Warning ** : %.8s is not UEFI Spec compliant (lowercase hex in name)\n", 696 | name); 697 | warn++; 698 | } 699 | } 700 | } 701 | if (warn) 702 | warningx("** Warning ** : please recreate these using efibootmgr to remove this warning."); 703 | } 704 | 705 | static void 706 | print_order(const char *name, uint16_t *order, int length) 707 | { 708 | int i; 709 | printf("%s: ", name); 710 | for (i=0; inum == b) 727 | return 1; 728 | } 729 | return 0; 730 | } 731 | 732 | static void 733 | print_error_arrow(char *buffer, off_t offset, char *fmt, ...) 734 | { 735 | va_list ap; 736 | size_t size; 737 | unsigned int i; 738 | 739 | va_start(ap, fmt); 740 | size = vfprintf(stderr, fmt, ap); 741 | va_end(ap); 742 | fprintf(stderr, "%s\n", buffer); 743 | 744 | for (i = 0; i < size + 2; i++) 745 | fprintf(stderr, " "); 746 | for (i = 0; i < offset; i++) 747 | fprintf(stderr, " "); 748 | fprintf(stderr, "^\n"); 749 | } 750 | 751 | static int 752 | parse_order(const char *prefix, char *buffer, uint16_t **order, size_t *length) 753 | { 754 | uint16_t *data; 755 | size_t data_size; 756 | size_t len = strlen(buffer); 757 | intptr_t end = (intptr_t)buffer + len + 1; 758 | 759 | int num = 0; 760 | char *buf = buffer; 761 | while ((intptr_t)buf < end) { 762 | size_t comma = strcspn(buf, ","); 763 | if (comma == 0) { 764 | off_t offset = (intptr_t)buf - (intptr_t)buffer; 765 | print_error_arrow(buffer, offset, "Malformed %s order", 766 | prefix); 767 | exit(8); 768 | } else { 769 | num++; 770 | } 771 | buf += comma + 1; 772 | } 773 | 774 | if (num == 0) { 775 | *order = NULL; 776 | *length = 0; 777 | return 0; 778 | } 779 | 780 | data = calloc(num, sizeof (*data)); 781 | if (!data) 782 | return -1; 783 | data_size = num * sizeof (*data); 784 | 785 | int i = 0; 786 | buf = buffer; 787 | while ((intptr_t)buf < end) { 788 | unsigned long result = 0; 789 | size_t comma = strcspn(buf, ","); 790 | 791 | buf[comma] = '\0'; 792 | char *endptr = NULL; 793 | result = strtoul(buf, &endptr, 16); 794 | if ((result == ULONG_MAX && errno == ERANGE) || 795 | (endptr && *endptr != '\0')) { 796 | off_t offset = (intptr_t)endptr - (intptr_t)buffer; 797 | print_error_arrow(buffer, offset, "Invalid %s order", 798 | prefix); 799 | free(data); 800 | exit(8); 801 | } 802 | if (result > 0xffff) { 803 | off_t offset = (intptr_t)buf - (intptr_t)buffer; 804 | warnx("Invalid %s order entry value: %lX", prefix, 805 | result); 806 | print_error_arrow(buffer, offset, "Invalid %s order", 807 | prefix); 808 | free(data); 809 | exit(8); 810 | } 811 | 812 | /* make sure this is an existing entry */ 813 | if (!is_current_entry(result)) { 814 | off_t offset = (intptr_t)buf - (intptr_t)buffer; 815 | print_error_arrow(buffer, offset, 816 | "Invalid %s order entry value", 817 | prefix); 818 | warnx("entry %04lX does not exist", result); 819 | free(data); 820 | exit(8); 821 | } 822 | 823 | data[i++] = result; 824 | buf[comma] = ','; 825 | buf += comma + 1; 826 | } 827 | 828 | *order = data; 829 | *length = data_size; 830 | return num; 831 | } 832 | 833 | static int 834 | construct_order(const char *name, char *order, int keep, 835 | uint16_t **ret_data, size_t *ret_data_size) 836 | { 837 | var_entry_t bo; 838 | int rc; 839 | uint16_t *data = NULL; 840 | size_t data_size = 0; 841 | 842 | rc = parse_order(name, order, (uint16_t **)&data, &data_size); 843 | if (rc < 0 || data_size == 0) { 844 | if (data) /* this can't actually happen, but covscan believes */ 845 | free(data); 846 | return rc; 847 | } 848 | 849 | if (!keep) { 850 | *ret_data = data; 851 | *ret_data_size = data_size; 852 | return 0; 853 | } 854 | 855 | rc = efi_get_variable(EFI_GLOBAL_GUID, name, 856 | &bo.data, &bo.data_size, &bo.attributes); 857 | if (rc < 0) { 858 | *ret_data = data; 859 | *ret_data_size = data_size; 860 | return 0; 861 | } 862 | 863 | /* latest apple firmware sets high bit which appears invalid 864 | * to the linux kernel if we write it back so lets zero it out 865 | * if it is set since it would be invalid to set it anyway */ 866 | bo.attributes = bo.attributes & ~(1 << 31); 867 | 868 | size_t new_data_size = data_size + bo.data_size; 869 | uint16_t *new_data = calloc(1, new_data_size); 870 | if (!new_data) { 871 | if (data) 872 | free(data); 873 | return -1; 874 | } 875 | 876 | memcpy(new_data, data, data_size); 877 | memcpy(new_data + (data_size / sizeof (*new_data)), bo.data, 878 | bo.data_size); 879 | 880 | free(bo.data); 881 | free(data); 882 | 883 | int new_data_start = data_size / sizeof (uint16_t); 884 | int new_data_end = new_data_size / sizeof (uint16_t); 885 | int i; 886 | for (i = 0; i < new_data_start; i++) { 887 | int j; 888 | for (j = new_data_start; j < new_data_end; j++) { 889 | if (new_data[i] == new_data[j]) { 890 | memcpy(new_data + j, new_data + j + 1, 891 | sizeof (uint16_t) * (new_data_end-j+1)); 892 | new_data_end -= 1; 893 | break; 894 | } 895 | } 896 | } 897 | *ret_data = new_data; 898 | *ret_data_size = new_data_end * sizeof (uint16_t); 899 | return 0; 900 | } 901 | 902 | static int 903 | set_order(const char *order_name, const char *prefix, int keep_old_entries) 904 | { 905 | uint8_t *data = NULL; 906 | size_t data_size = 0; 907 | char *name; 908 | int rc; 909 | 910 | if (!opts.order) 911 | return 0; 912 | 913 | rc = construct_order(order_name, opts.order, keep_old_entries, 914 | (uint16_t **)&data, &data_size); 915 | if (rc < 0 || data_size == 0) { 916 | if (data) /* this can't happen, but clang analyzer believes */ 917 | free(data); 918 | return rc; 919 | } 920 | 921 | rc = asprintf(&name, "%sOrder", prefix); 922 | if (rc < 0) 923 | goto err; 924 | 925 | rc = efi_set_variable(EFI_GLOBAL_GUID, name, data, data_size, 926 | EFI_VARIABLE_NON_VOLATILE | 927 | EFI_VARIABLE_BOOTSERVICE_ACCESS | 928 | EFI_VARIABLE_RUNTIME_ACCESS, 929 | 0644); 930 | free(name); 931 | err: 932 | free(data); 933 | return rc; 934 | } 935 | 936 | #define ev_bits(val, mask, shift) \ 937 | (((val) & ((mask) << (shift))) >> (shift)) 938 | 939 | static inline char * 940 | ucs2_to_utf8(const uint16_t * const chars, ssize_t limit) 941 | { 942 | ssize_t i, j; 943 | char *ret; 944 | 945 | ret = alloca(limit * 6 + 1); 946 | if (!ret) 947 | return NULL; 948 | memset(ret, 0, limit * 6 +1); 949 | 950 | for (i=0, j=0; i < (limit >= 0 ? limit : i+1) && chars[i]; i++,j++) { 951 | if (chars[i] <= 0x7f) { 952 | ret[j] = chars[i]; 953 | } else if (chars[i] > 0x7f && chars[i] <= 0x7ff) { 954 | ret[j++] = 0xc0 | ev_bits(chars[i], 0x1f, 6); 955 | ret[j] = 0x80 | ev_bits(chars[i], 0x3f, 0); 956 | } else if (chars[i] > 0x7ff) { 957 | ret[j++] = 0xe0 | ev_bits(chars[i], 0xf, 12); 958 | ret[j++] = 0x80 | ev_bits(chars[i], 0x3f, 6); 959 | ret[j] = 0x80| ev_bits(chars[i], 0x3f, 0); 960 | } 961 | } 962 | ret[j] = '\0'; 963 | return strdup(ret); 964 | } 965 | 966 | static void 967 | show_var_path(efi_load_option *load_option, size_t boot_data_size) 968 | { 969 | char *text_path = NULL; 970 | size_t text_path_len = 0; 971 | uint16_t pathlen; 972 | ssize_t rc; 973 | efidp dp = NULL; 974 | unsigned char *optional_data = NULL; 975 | size_t optional_data_len=0; 976 | bool is_shim = false; 977 | const char * const shim_path_segments[] = { 978 | "/File(\\EFI\\", "\\shim", ".efi)", NULL 979 | }; 980 | 981 | pathlen = efi_loadopt_pathlen(load_option, 982 | boot_data_size); 983 | dp = efi_loadopt_path(load_option, boot_data_size); 984 | rc = efidp_format_device_path((unsigned char *)text_path, 985 | text_path_len, dp, pathlen); 986 | if (rc < 0) { 987 | warning("Could not parse device path"); 988 | return; 989 | } 990 | rc += 1; 991 | 992 | text_path_len = rc; 993 | text_path = calloc(1, rc); 994 | if (!text_path) { 995 | warning("Could not parse device path"); 996 | return; 997 | } 998 | 999 | rc = efidp_format_device_path((unsigned char *)text_path, 1000 | text_path_len, dp, pathlen); 1001 | if (rc >= 0) { 1002 | printf("\t%s", text_path); 1003 | 1004 | char *a = text_path; 1005 | for (int i = 0; a && shim_path_segments[i] != NULL; i++) { 1006 | a = strstr(a, shim_path_segments[i]); 1007 | if (a) 1008 | a += strlen(shim_path_segments[i]); 1009 | } 1010 | if (a && a[0] == '\0') 1011 | is_shim = true; 1012 | } 1013 | 1014 | free(text_path); 1015 | if (rc < 0) { 1016 | warning("Could not parse device path"); 1017 | return; 1018 | } 1019 | 1020 | /* Print optional data */ 1021 | rc = efi_loadopt_optional_data(load_option, boot_data_size, 1022 | &optional_data, &optional_data_len); 1023 | if (rc < 0) { 1024 | warning("Could not parse optional data"); 1025 | return; 1026 | } 1027 | 1028 | typedef ssize_t (*parser_t)(char *buffer, size_t buffer_size, 1029 | uint8_t *p, uint64_t length); 1030 | parser_t parser = NULL; 1031 | if (is_shim && optional_data_len) { 1032 | char *a = ucs2_to_utf8((uint16_t*)optional_data, 1033 | optional_data_len/2); 1034 | if (!a) { 1035 | warning("Could not parse optional data"); 1036 | return; 1037 | } 1038 | text_path = calloc(1, sizeof(" File(.") 1039 | + strlen(a) 1040 | + strlen(")")); 1041 | if (!text_path) { 1042 | free(a); 1043 | warning("Could not parse optional data"); 1044 | return; 1045 | } 1046 | char *b; 1047 | 1048 | b = stpcpy(text_path, " File(."); 1049 | b = stpcpy(b, a); 1050 | stpcpy(b, ")"); 1051 | free(a); 1052 | } else if (opts.unicode) { 1053 | text_path = ucs2_to_utf8((uint16_t*)optional_data, 1054 | optional_data_len/2); 1055 | if (!text_path) { 1056 | warning("Could not parse optional data"); 1057 | return; 1058 | } 1059 | } else if (optional_data_len == sizeof(efi_guid_t)) { 1060 | parser = parse_efi_guid; 1061 | } else { 1062 | parser = parse_raw_text; 1063 | } 1064 | 1065 | if (parser) { 1066 | rc = parser(NULL, 0, optional_data, optional_data_len); 1067 | if (rc < 0) { 1068 | warning("Could not parse optional data"); 1069 | return; 1070 | } 1071 | rc += 1; 1072 | text_path_len = rc; 1073 | text_path = calloc(1, rc); 1074 | if (!text_path) { 1075 | warning("Could not parse optional data"); 1076 | return; 1077 | } 1078 | rc = parser(text_path, text_path_len, 1079 | optional_data, optional_data_len); 1080 | if (rc < 0) { 1081 | warning("Could not parse device path"); 1082 | free(text_path); 1083 | return; 1084 | } 1085 | } 1086 | printf("%s", text_path); 1087 | free(text_path); 1088 | printf("\n"); 1089 | 1090 | const_efidp node = dp; 1091 | if (opts.verbose >= 1) 1092 | printf(" dp: "); 1093 | for (rc = 1; opts.verbose >= 1 && rc > 0; ) { 1094 | ssize_t sz; 1095 | const_efidp next = NULL; 1096 | const uint8_t * const data = (const uint8_t * const)node; 1097 | 1098 | rc = efidp_next_node(node, &next); 1099 | if (rc < 0) { 1100 | warning("Could not iterate device path"); 1101 | return; 1102 | } 1103 | 1104 | sz = efidp_node_size(node); 1105 | if (sz <= 0) { 1106 | warning("Could not iterate device path"); 1107 | return; 1108 | } 1109 | 1110 | for (ssize_t j = 0; j < sz; j++) 1111 | printf("%02hhx%s", data[j], j == sz - 1 ? "" : " "); 1112 | printf("%s", rc == 0 ? "\n" : " / "); 1113 | 1114 | node = next; 1115 | } 1116 | if (opts.verbose >= 1 && optional_data_len) 1117 | printf(" data: "); 1118 | for (unsigned int j = 0; opts.verbose >= 1 && j < optional_data_len; j++) 1119 | printf("%02hhx%s", optional_data[j], j == optional_data_len - 1 ? "\n" : " "); 1120 | } 1121 | 1122 | static void 1123 | show_vars(const char *prefix) 1124 | { 1125 | list_t *pos; 1126 | var_entry_t *boot; 1127 | const unsigned char *description; 1128 | efi_load_option *load_option; 1129 | 1130 | list_for_each(pos, &entry_list) { 1131 | boot = list_entry(pos, var_entry_t, list); 1132 | load_option = (efi_load_option *)boot->data; 1133 | description = efi_loadopt_desc(load_option, boot->data_size); 1134 | if (boot->name) 1135 | printf("%s", boot->name); 1136 | else 1137 | printf("%s%04X", prefix, boot->num); 1138 | 1139 | printf("%c ", (efi_loadopt_attrs(load_option) 1140 | & LOAD_OPTION_ACTIVE) ? '*' : ' '); 1141 | printf("%s", description); 1142 | 1143 | show_var_path(load_option, boot->data_size); 1144 | 1145 | fflush(stdout); 1146 | } 1147 | } 1148 | 1149 | static void 1150 | show_order(const char *name) 1151 | { 1152 | int rc; 1153 | var_entry_t *order = NULL; 1154 | uint16_t *data; 1155 | 1156 | rc = read_order(name, &order); 1157 | cond_warning(opts.verbose >= 2 && rc < 0, 1158 | "Could not read variable '%s'", name); 1159 | 1160 | if (rc < 0) { 1161 | if (errno == ENOENT) { 1162 | if (!strcmp(name, "BootOrder")) 1163 | printf("No BootOrder is set; firmware will attempt recovery\n"); 1164 | else 1165 | printf("No %s is set\n", name); 1166 | } else 1167 | perror("show_order()"); 1168 | return; 1169 | } 1170 | 1171 | /* We've now got an array (in order->data) of the order. First add 1172 | * our entry, then copy the old array. 1173 | */ 1174 | data = (uint16_t *)order->data; 1175 | if (order->data_size) { 1176 | print_order(name, data, 1177 | order->data_size / sizeof(uint16_t)); 1178 | free(order->data); 1179 | } 1180 | free(order); 1181 | } 1182 | 1183 | static var_entry_t * 1184 | get_entry(list_t *entries, uint16_t num) 1185 | { 1186 | list_t *pos; 1187 | var_entry_t *entry = NULL; 1188 | 1189 | list_for_each(pos, entries) { 1190 | entry = list_entry(pos, var_entry_t, list); 1191 | if (entry->num != num) { 1192 | entry = NULL; 1193 | continue; 1194 | } 1195 | return entry; 1196 | } 1197 | 1198 | return entry; 1199 | } 1200 | 1201 | static int 1202 | update_entry_attr(var_entry_t *entry, uint64_t attr, bool set) 1203 | { 1204 | efi_load_option *load_option; 1205 | uint64_t attrs; 1206 | int rc; 1207 | 1208 | load_option = (efi_load_option *)entry->data; 1209 | attrs = efi_loadopt_attrs(load_option); 1210 | 1211 | if ((set && (attrs & attr)) || (!set && !(attrs & attr))) 1212 | return 0; 1213 | 1214 | if (set) 1215 | efi_loadopt_attr_set(load_option, attr); 1216 | else 1217 | efi_loadopt_attr_clear(load_option, attr); 1218 | 1219 | rc = efi_set_variable(entry->guid, entry->name, 1220 | entry->data, entry->data_size, 1221 | entry->attributes, 0644); 1222 | if (rc < 0) { 1223 | char *guid = NULL; 1224 | int err = errno; 1225 | 1226 | efi_guid_to_str(&entry->guid, &guid); 1227 | errno = err; 1228 | efi_error("efi_set_variable(%s,%s,...)", 1229 | guid, entry->name); 1230 | } 1231 | 1232 | return rc; 1233 | } 1234 | 1235 | static int 1236 | set_active_state(const char *prefix) 1237 | { 1238 | var_entry_t *entry; 1239 | 1240 | entry = get_entry(&entry_list, opts.num); 1241 | if (!entry) { 1242 | /* if we reach here then the number supplied was not found */ 1243 | warnx("%s entry %x not found", prefix, opts.num); 1244 | errno = ENOENT; 1245 | return -1; 1246 | } 1247 | 1248 | return update_entry_attr(entry, LOAD_OPTION_ACTIVE, opts.active); 1249 | } 1250 | 1251 | static int 1252 | set_force_reconnect(const char *prefix) 1253 | { 1254 | var_entry_t *entry; 1255 | 1256 | entry = get_entry(&entry_list, opts.num); 1257 | if (!entry) { 1258 | /* if we reach here then the number supplied was not found */ 1259 | warnx("%s entry %x not found", prefix, opts.num); 1260 | errno = ENOENT; 1261 | return -1; 1262 | } 1263 | 1264 | return update_entry_attr(entry, LOAD_OPTION_FORCE_RECONNECT, 1265 | opts.reconnect > 0); 1266 | } 1267 | 1268 | static int 1269 | get_mirror(int which, int *below4g, int *above4g, int *mirrorstatus) 1270 | { 1271 | int rc; 1272 | uint8_t *data; 1273 | ADDRESS_RANGE_MIRROR_VARIABLE_DATA *abm; 1274 | size_t data_size; 1275 | uint32_t attributes; 1276 | char *name; 1277 | 1278 | if (which) 1279 | name = ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST; 1280 | else 1281 | name = ADDRESS_RANGE_MIRROR_VARIABLE_CURRENT; 1282 | 1283 | rc = efi_get_variable(ADDRESS_RANGE_MIRROR_VARIABLE_GUID, name, 1284 | &data, &data_size, &attributes); 1285 | if (rc == 0) { 1286 | abm = (ADDRESS_RANGE_MIRROR_VARIABLE_DATA *)data; 1287 | if (!which && abm->mirror_version != MIRROR_VERSION) { 1288 | rc = 2; 1289 | } 1290 | *below4g = abm->mirror_memory_below_4gb; 1291 | *above4g = abm->mirror_amount_above_4gb; 1292 | *mirrorstatus = abm->mirror_status; 1293 | free(data); 1294 | } else { 1295 | cond_warning(opts.verbose >= 2, 1296 | "Could not read variable '%s'", name); 1297 | errno = 0; 1298 | } 1299 | return rc; 1300 | } 1301 | 1302 | static int 1303 | set_mirror(int below4g, int above4g) 1304 | { 1305 | int s, status, rc; 1306 | uint8_t *data; 1307 | ADDRESS_RANGE_MIRROR_VARIABLE_DATA abm; 1308 | size_t data_size; 1309 | uint32_t attributes; 1310 | int oldbelow4g, oldabove4g; 1311 | 1312 | if ((s = get_mirror(0, &oldbelow4g, &oldabove4g, &status)) != 0) { 1313 | if (s == 2) 1314 | warningx("** Warning ** : unrecognised version for memory mirror i/f"); 1315 | else 1316 | warningx("** Warning ** : platform does not support memory mirror"); 1317 | return s; 1318 | } 1319 | 1320 | below4g = opts.set_mirror_lo ? below4g : oldbelow4g; 1321 | above4g = opts.set_mirror_hi ? above4g : oldabove4g; 1322 | if (oldbelow4g == below4g && oldabove4g == above4g) 1323 | return 0; 1324 | 1325 | data = (uint8_t *)&abm; 1326 | data_size = sizeof (abm); 1327 | attributes = EFI_VARIABLE_NON_VOLATILE 1328 | | EFI_VARIABLE_BOOTSERVICE_ACCESS 1329 | | EFI_VARIABLE_RUNTIME_ACCESS; 1330 | 1331 | abm.mirror_version = MIRROR_VERSION; 1332 | abm.mirror_amount_above_4gb = above4g; 1333 | abm.mirror_memory_below_4gb = below4g; 1334 | abm.mirror_status = 0; 1335 | rc = efi_set_variable(ADDRESS_RANGE_MIRROR_VARIABLE_GUID, 1336 | ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST, data, 1337 | data_size, attributes, 0644); 1338 | if (rc < 0) 1339 | efi_error("efi_set_variable() failed"); 1340 | return rc; 1341 | } 1342 | 1343 | static void 1344 | show_mirror(void) 1345 | { 1346 | int status; 1347 | int below4g = 0, above4g = 0; 1348 | int rbelow4g = 0, rabove4g = 0; 1349 | 1350 | if (get_mirror(0, &below4g, &above4g, &status) == 0) { 1351 | if (status == 0) { 1352 | printf("MirroredPercentageAbove4G: %d.%.2d\n", 1353 | above4g/100, above4g%100); 1354 | printf("MirrorMemoryBelow4GB: %s\n", 1355 | below4g ? "true" : "false"); 1356 | } else { 1357 | printf("MirrorStatus: "); 1358 | switch (status) { 1359 | case 1: 1360 | printf("Platform does not support address range mirror\n"); 1361 | break; 1362 | case 2: 1363 | printf("Invalid version number\n"); 1364 | break; 1365 | case 3: 1366 | printf("MirroredMemoryAbove4GB > 50.00%%\n"); 1367 | break; 1368 | case 4: 1369 | printf("DIMM configuration does not allow mirror\n"); 1370 | break; 1371 | case 5: 1372 | printf("OEM specific method\n"); 1373 | break; 1374 | default: 1375 | printf("%u\n", status); 1376 | break; 1377 | } 1378 | printf("DesiredMirroredPercentageAbove4G: %d.%.2d\n", 1379 | above4g/100, above4g%100); 1380 | printf("DesiredMirrorMemoryBelow4GB: %s\n", 1381 | below4g ? "true" : "false"); 1382 | } 1383 | } 1384 | if ((get_mirror(1, &rbelow4g, &rabove4g, &status) == 0) && 1385 | (above4g != rabove4g || below4g != rbelow4g)) { 1386 | printf("RequestMirroredPercentageAbove4G: %d.%.2d\n", 1387 | rabove4g/100, rabove4g%100); 1388 | printf("RequestMirrorMemoryBelow4GB: %s\n", 1389 | rbelow4g ? "true" : "false"); 1390 | } 1391 | } 1392 | 1393 | static int 1394 | list_supported_signature_types(void) 1395 | { 1396 | var_entry_t entry = { 0, }; 1397 | int rc; 1398 | size_t n_entries; 1399 | unsigned int i; 1400 | 1401 | rc = efi_get_variable(EFI_GLOBAL_GUID, "SignatureSupport", 1402 | &entry.data, &entry.data_size, &entry.attributes); 1403 | if (rc == ENOENT) { 1404 | warning("Firmware does not support any signature types"); 1405 | return 0; 1406 | } 1407 | if (rc < 0) { 1408 | efi_error("efi_get_variable failed"); 1409 | return -1; 1410 | } 1411 | if (entry.data_size % sizeof(efi_guid_t) != 0) 1412 | warning("SignatureSupport variable has suspicious size %zu", 1413 | entry.data_size); 1414 | 1415 | n_entries = entry.data_size / sizeof(efi_guid_t); 1416 | 1417 | for (i = 0; i < n_entries; i++) { 1418 | efi_guid_t *guid = &((efi_guid_t *)entry.data)[i]; 1419 | char *guidname = NULL; 1420 | 1421 | rc = efi_guid_to_name(guid, &guidname); 1422 | if (rc < 0) { 1423 | efi_error("efi_guid_to_name failed"); 1424 | return -1; 1425 | } 1426 | printf("%s", guidname); 1427 | free(guidname); 1428 | guidname = NULL; 1429 | 1430 | if (verbose >= 1) { 1431 | rc = efi_guid_to_str(guid, &guidname); 1432 | 1433 | if (rc < 0) { 1434 | putchar('\n'); 1435 | efi_error("efi_guid_to_str failed"); 1436 | return -1; 1437 | } 1438 | printf(" %s", guidname); 1439 | free(guidname); 1440 | } 1441 | 1442 | putchar('\n'); 1443 | } 1444 | 1445 | return 0; 1446 | } 1447 | 1448 | static void 1449 | usage() 1450 | { 1451 | printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION); 1452 | printf("usage: efibootmgr [options]\n"); 1453 | printf("\t-a | --active Set bootnum active.\n"); 1454 | printf("\t-A | --inactive Set bootnum inactive.\n"); 1455 | printf("\t-b | --bootnum XXXX Modify BootXXXX (hex).\n"); 1456 | printf("\t-B | --delete-bootnum Delete bootnum.\n"); 1457 | printf("\t-c | --create Create new variable bootnum and add to bootorder at index (-I).\n"); 1458 | printf("\t-C | --create-only Create new variable bootnum and do not add to bootorder.\n"); 1459 | printf("\t-d | --disk disk Disk containing boot loader (defaults to /dev/sda).\n"); 1460 | printf("\t-D | --remove-dups Remove duplicate values from BootOrder.\n"); 1461 | printf("\t-e | --edd [1|3] Force boot entries to be created using EDD 1.0 or 3.0 info.\n"); 1462 | printf("\t-E | --edd-device num EDD 1.0 device number (defaults to 0x80).\n"); 1463 | printf("\t --full-dev-path Use a full device path.\n"); 1464 | printf("\t --file-dev-path Use an abbreviated File() device path.\n"); 1465 | printf("\t-f | --reconnect Re-connect devices after driver is loaded.\n"); 1466 | printf("\t-F | --no-reconnect Do not re-connect devices after driver is loaded.\n"); 1467 | printf("\t-g | --gpt Force disk with invalid PMBR to be treated as GPT.\n"); 1468 | printf("\t-i | --iface name Create a netboot entry for the named interface.\n"); 1469 | printf("\t-I | --index number When creating an entry, insert it in bootorder at specified position (default: 0).\n"); 1470 | printf("\t-l | --loader name (Defaults to \""DEFAULT_LOADER"\").\n"); 1471 | printf("\t-L | --label label Boot manager display label (defaults to \"Linux\").\n"); 1472 | printf("\t-m | --mirror-below-4G t|f Mirror memory below 4GB.\n"); 1473 | printf("\t-M | --mirror-above-4G X Percentage memory to mirror above 4GB.\n"); 1474 | printf("\t-n | --bootnext XXXX Set BootNext to XXXX (hex).\n"); 1475 | printf("\t-N | --delete-bootnext Delete BootNext.\n"); 1476 | printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,... Explicitly set BootOrder (hex).\n"); 1477 | printf("\t-O | --delete-bootorder Delete BootOrder.\n"); 1478 | printf("\t-p | --part part Partition containing loader (defaults to 1 on partitioned devices).\n"); 1479 | printf("\t-q | --quiet Be quiet.\n"); 1480 | printf("\t-r | --driver Operate on Driver variables, not Boot Variables.\n"); 1481 | printf("\t-t | --timeout seconds Set boot manager timeout waiting for user input.\n"); 1482 | printf("\t-T | --delete-timeout Delete Timeout.\n"); 1483 | printf("\t-u | --unicode | --UCS-2 Handle extra args as UCS-2 (default is ASCII).\n"); 1484 | printf("\t-v | --verbose Print additional information.\n"); 1485 | printf("\t-V | --version Return version and exit.\n"); 1486 | printf("\t-y | --sysprep Operate on SysPrep variables, not Boot Variables.\n"); 1487 | printf("\t-@ | --append-binary-args file Append extra args from file (use \"-\" for stdin).\n"); 1488 | printf("\t-h | --help Show help/usage.\n"); 1489 | } 1490 | 1491 | static void 1492 | set_default_opts() 1493 | { 1494 | memset(&opts, 0, sizeof(opts)); 1495 | opts.num = -1; /* auto-detect */ 1496 | opts.bootnext = -1; /* Don't set it */ 1497 | opts.active = -1; /* Don't set it */ 1498 | opts.reconnect = -1; /* Don't set it */ 1499 | opts.timeout = -1; /* Don't set it */ 1500 | opts.edd10_devicenum = 0x80; 1501 | opts.loader = DEFAULT_LOADER; 1502 | opts.label = (unsigned char *)"Linux"; 1503 | opts.disk = "/dev/sda"; 1504 | opts.part = -1; 1505 | } 1506 | 1507 | static void 1508 | parse_opts(int argc, char **argv) 1509 | { 1510 | int c, rc; 1511 | unsigned int num; 1512 | int snum; 1513 | float fnum; 1514 | int option_index = 0; 1515 | long lindex; 1516 | 1517 | while (1) 1518 | { 1519 | static struct option long_options[] = 1520 | /* name, has_arg, flag, val */ 1521 | { 1522 | {"active", no_argument, 0, 'a'}, 1523 | {"inactive", no_argument, 0, 'A'}, 1524 | {"bootnum", required_argument, 0, 'b'}, 1525 | {"delete-bootnum", no_argument, 0, 'B'}, 1526 | {"create", no_argument, 0, 'c'}, 1527 | {"create-only", no_argument, 0, 'C'}, 1528 | {"disk", required_argument, 0, 'd'}, 1529 | {"remove-dups", no_argument, 0, 'D'}, 1530 | {"edd", required_argument, 0, 'e'}, 1531 | {"edd30", required_argument, 0, 'e'}, 1532 | {"edd-device", required_argument, 0, 'E'}, 1533 | {"full-dev-path", no_argument, 0, 0}, 1534 | {"file-dev-path", no_argument, 0, 0}, 1535 | {"reconnect", no_argument, 0, 'f'}, 1536 | {"no-reconnect", no_argument, 0, 'F'}, 1537 | {"gpt", no_argument, 0, 'g'}, 1538 | {"iface", required_argument, 0, 'i'}, 1539 | {"index", required_argument, 0, 'I'}, 1540 | {"keep", no_argument, 0, 'k'}, 1541 | {"loader", required_argument, 0, 'l'}, 1542 | {"label", required_argument, 0, 'L'}, 1543 | {"mirror-below-4G", required_argument, 0, 'm'}, 1544 | {"mirror-above-4G", required_argument, 0, 'M'}, 1545 | {"bootnext", required_argument, 0, 'n'}, 1546 | {"delete-bootnext", no_argument, 0, 'N'}, 1547 | {"bootorder", required_argument, 0, 'o'}, 1548 | {"delete-bootorder", no_argument, 0, 'O'}, 1549 | {"part", required_argument, 0, 'p'}, 1550 | {"quiet", no_argument, 0, 'q'}, 1551 | {"driver", no_argument, 0, 'r'}, 1552 | {"list-signature-types", no_argument, 0, 's'}, 1553 | {"timeout", required_argument, 0, 't'}, 1554 | {"delete-timeout", no_argument, 0, 'T'}, 1555 | {"unicode", no_argument, 0, 'u'}, 1556 | {"UCS-2", no_argument, 0, 'u'}, 1557 | {"verbose", optional_argument, 0, 'v'}, 1558 | {"version", no_argument, 0, 'V'}, 1559 | {"sysprep", no_argument, 0, 'y'}, 1560 | {"append-binary-args", required_argument, 0, '@'}, 1561 | {"help", no_argument, 0, 'h'}, 1562 | {0, 0, 0, 0} 1563 | }; 1564 | 1565 | c = getopt_long(argc, argv, 1566 | "aAb:BcCd:De:E:fFgi:I:kl:L:m:M:n:No:Op:qrst:Tuv::Vwy@:h", 1567 | long_options, &option_index); 1568 | if (c == -1) 1569 | break; 1570 | 1571 | switch (c) { 1572 | case '@': 1573 | opts.extra_opts_file = optarg; 1574 | break; 1575 | case 'a': 1576 | opts.active = 1; 1577 | break; 1578 | case 'A': 1579 | opts.active = 0; 1580 | break; 1581 | case 'B': 1582 | opts.delete = 1; 1583 | break; 1584 | case 'b': { 1585 | char *endptr = NULL; 1586 | unsigned long result; 1587 | 1588 | if (!optarg) { 1589 | errorx(29, "--%s requires an argument", 1590 | long_options[option_index]); 1591 | break; 1592 | } 1593 | 1594 | result = strtoul(optarg, &endptr, 16); 1595 | if ((result == ULONG_MAX && errno == ERANGE) || 1596 | (endptr && *endptr != '\0')) { 1597 | off_t offset = (intptr_t)endptr 1598 | - (intptr_t)optarg; 1599 | print_error_arrow(optarg, offset, 1600 | "Invalid bootnum value"); 1601 | conditional_error_reporter(opts.verbose >= 1, 1602 | 1); 1603 | exit(28); 1604 | } 1605 | if (result > 0xffff) 1606 | errorx(29, "Invalid bootnum value: %lX\n", 1607 | result); 1608 | 1609 | opts.num = result; 1610 | break; 1611 | } 1612 | case 'c': 1613 | opts.create = 1; 1614 | break; 1615 | case 'C': 1616 | opts.create = 1; 1617 | opts.no_order = 1; 1618 | break; 1619 | case 'D': 1620 | opts.deduplicate = 1; 1621 | break; 1622 | case 'd': 1623 | opts.disk = optarg; 1624 | break; 1625 | case 'e': 1626 | rc = sscanf(optarg, "%d", &snum); 1627 | if (rc != 1) 1628 | errorx(30, "invalid numeric value %s\n", 1629 | optarg); 1630 | 1631 | if (snum != EFIBOOTMGR_PATH_ABBREV_EDD10 && 1632 | snum != EFIBOOTMGR_PATH_ABBREV_NONE) 1633 | errorx(31, "invalid EDD version %d\n", snum); 1634 | 1635 | if (opts.abbreviate_path != EFIBOOTMGR_PATH_ABBREV_UNSPECIFIED && 1636 | opts.abbreviate_path != snum) 1637 | errx(41, "contradicting --full-device-path/--file-device-path/-e options"); 1638 | 1639 | opts.abbreviate_path = snum; 1640 | break; 1641 | case 'E': 1642 | rc = sscanf(optarg, "%x", &num); 1643 | if (rc == 1) 1644 | opts.edd10_devicenum = num; 1645 | else 1646 | errorx(32, "invalid hex value %s\n", optarg); 1647 | break; 1648 | case 'f': 1649 | opts.reconnect = 1; 1650 | break; 1651 | case 'F': 1652 | opts.reconnect = 0; 1653 | break; 1654 | case 'g': 1655 | opts.forcegpt = 1; 1656 | break; 1657 | 1658 | case 'h': 1659 | usage(); 1660 | exit(0); 1661 | break; 1662 | 1663 | case 'i': 1664 | opts.iface = optarg; 1665 | opts.ip_version = EFIBOOTMGR_IPV4; 1666 | opts.ip_addr_origin = EFIBOOTMGR_IPV4_ORIGIN_DHCP; 1667 | break; 1668 | case 'I': 1669 | if (!optarg) { 1670 | errorx(1, "--%s requires an argument", 1671 | long_options[option_index]); 1672 | } 1673 | lindex = atol(optarg); 1674 | if (lindex < 0 || lindex > UINT16_MAX) { 1675 | errorx(1, "invalid numeric value %s\n", 1676 | optarg); 1677 | } 1678 | opts.index = (uint16_t)lindex; 1679 | break; 1680 | case 'k': 1681 | opts.keep_old_entries = 1; 1682 | break; 1683 | case 'l': 1684 | opts.loader = optarg; 1685 | break; 1686 | case 'L': 1687 | opts.label = (unsigned char *)optarg; 1688 | opts.explicit_label = 1; 1689 | break; 1690 | case 'm': 1691 | 1692 | if (!optarg) { 1693 | errorx(33, "--%s requires an argument", 1694 | long_options[option_index]); 1695 | break; 1696 | } 1697 | 1698 | opts.set_mirror_lo = 1; 1699 | 1700 | switch (optarg[0]) { 1701 | case '1': case 'y': case 't': 1702 | opts.below4g = 1; 1703 | break; 1704 | case '0': case 'n': case 'f': 1705 | opts.below4g = 0; 1706 | break; 1707 | default: 1708 | errorx(33, "invalid boolean value %s\n", 1709 | optarg); 1710 | } 1711 | break; 1712 | case 'M': 1713 | opts.set_mirror_hi = 1; 1714 | rc = sscanf(optarg, "%f", &fnum); 1715 | if (rc == 1 && fnum <= 50 && fnum >= 0) 1716 | /* percent to basis points */ 1717 | opts.above4g = fnum * 100; 1718 | else 1719 | errorx(34, "invalid numeric value %s\n", 1720 | optarg); 1721 | break; 1722 | case 'N': 1723 | opts.delete_bootnext = 1; 1724 | break; 1725 | case 'n': { 1726 | char *endptr = NULL; 1727 | unsigned long result; 1728 | 1729 | if (!optarg) { 1730 | errorx(36, "--%s requires an argument", 1731 | long_options[option_index]); 1732 | break; 1733 | } 1734 | 1735 | result = strtoul(optarg, &endptr, 16); 1736 | if ((result == ULONG_MAX && errno == ERANGE) || 1737 | (endptr && *endptr != '\0')) { 1738 | off_t offset = (intptr_t)endptr 1739 | - (intptr_t)optarg; 1740 | print_error_arrow(optarg, offset, 1741 | "Invalid BootNext value"); 1742 | conditional_error_reporter(opts.verbose >= 1, 1743 | 1); 1744 | exit(35); 1745 | } 1746 | if (result > 0xffff) 1747 | errorx(36, "Invalid BootNext value: %lX\n", 1748 | result); 1749 | opts.bootnext = result; 1750 | break; 1751 | } 1752 | case 'o': 1753 | opts.order = optarg; 1754 | break; 1755 | case 'O': 1756 | opts.delete_order = 1; 1757 | break; 1758 | case 'p': 1759 | rc = sscanf(optarg, "%u", &num); 1760 | if (rc == 1) 1761 | opts.part = num; 1762 | else 1763 | errorx(37, "invalid numeric value %s\n", 1764 | optarg); 1765 | break; 1766 | case 'q': 1767 | opts.quiet = 1; 1768 | break; 1769 | case 'r': 1770 | opts.driver = 1; 1771 | break; 1772 | case 's': 1773 | opts.list_supported_signature_types = 1; 1774 | break; 1775 | case 't': 1776 | rc = sscanf(optarg, "%u", &num); 1777 | if (rc == 1) { 1778 | opts.timeout = num; 1779 | opts.set_timeout = 1; 1780 | } else { 1781 | errorx(38, "invalid numeric value %s\n", 1782 | optarg); 1783 | } 1784 | break; 1785 | case 'T': 1786 | opts.delete_timeout = 1; 1787 | break; 1788 | case 'u': 1789 | opts.unicode = 1; 1790 | break; 1791 | case 'v': 1792 | opts.verbose += 1; 1793 | if (optarg) { 1794 | if (!strcmp(optarg, "v")) 1795 | opts.verbose = 1; 1796 | if (!strcmp(optarg, "vv")) 1797 | opts.verbose = 2; 1798 | rc = sscanf(optarg, "%u", &num); 1799 | if (rc == 1) 1800 | opts.verbose = num; 1801 | else 1802 | errorx(39, 1803 | "invalid numeric value %s\n", 1804 | optarg); 1805 | } 1806 | efi_set_verbose(opts.verbose - 1, stderr); 1807 | break; 1808 | case 'V': 1809 | opts.showversion = 1; 1810 | break; 1811 | 1812 | case 'y': 1813 | opts.sysprep = 1; 1814 | break; 1815 | 1816 | default: 1817 | if (!strcmp(long_options[option_index].name, "full-dev-path")) { 1818 | if (opts.abbreviate_path != EFIBOOTMGR_PATH_ABBREV_UNSPECIFIED && 1819 | opts.abbreviate_path != EFIBOOTMGR_PATH_ABBREV_NONE) 1820 | errx(41, "contradicting --full-dev-path/--file-dev-path/-e options"); 1821 | opts.abbreviate_path = EFIBOOTMGR_PATH_ABBREV_NONE; 1822 | } else if (!strcmp(long_options[option_index].name, "file-dev-path")) { 1823 | if (opts.abbreviate_path != EFIBOOTMGR_PATH_ABBREV_UNSPECIFIED && 1824 | opts.abbreviate_path != EFIBOOTMGR_PATH_ABBREV_FILE) 1825 | errx(41, "contradicting --full-dev-path/--file-dev-path/-e options"); 1826 | opts.abbreviate_path = EFIBOOTMGR_PATH_ABBREV_FILE; 1827 | } else { 1828 | usage(); 1829 | exit(1); 1830 | } 1831 | } 1832 | } 1833 | 1834 | if (optind < argc) { 1835 | opts.argc = argc; 1836 | opts.argv = argv; 1837 | opts.optind = optind; 1838 | } 1839 | } 1840 | 1841 | int 1842 | main(int argc, char **argv) 1843 | { 1844 | char **names = NULL; 1845 | var_entry_t *new_entry = NULL; 1846 | int num; 1847 | int ret = 0; 1848 | ebm_mode mode = boot; 1849 | char *prefices[] = { 1850 | "Boot", 1851 | "Driver", 1852 | "SysPrep", 1853 | }; 1854 | char *order_name[] = { 1855 | "BootOrder", 1856 | "DriverOrder", 1857 | "SysPrepOrder" 1858 | }; 1859 | 1860 | set_default_opts(); 1861 | parse_opts(argc, argv); 1862 | if (opts.showversion) { 1863 | printf("version %s\n", EFIBOOTMGR_VERSION); 1864 | return 0; 1865 | } 1866 | 1867 | verbose = opts.verbose; 1868 | 1869 | if (opts.list_supported_signature_types) { 1870 | int rc = list_supported_signature_types(); 1871 | if (rc < 0) 1872 | errorx(40, "Could not read and display supported signature types\n"); 1873 | exit(0); 1874 | } 1875 | 1876 | if (opts.sysprep && opts.driver) 1877 | errx(25, "--sysprep and --driver may not be used together."); 1878 | 1879 | if (opts.sysprep || opts.driver) { 1880 | if (opts.bootnext >= 0 || opts.delete_bootnext) 1881 | errx(26, "%s mode does not support BootNext options.", 1882 | opts.sysprep ? "--sysprep": "--driver"); 1883 | 1884 | if (opts.timeout >= 0 || opts.delete_timeout) 1885 | errx(27, "%s mode does not support timeout options.", 1886 | opts.sysprep ? "--sysprep": "--driver"); 1887 | 1888 | if (opts.sysprep) 1889 | mode = sysprep; 1890 | 1891 | if (opts.driver) 1892 | mode = driver; 1893 | } 1894 | 1895 | if (opts.reconnect > 0 && !opts.driver) 1896 | errorx(30, "--reconnect is supported only for driver entries."); 1897 | 1898 | if (!efi_variables_supported()) 1899 | errorx(2, "EFI variables are not supported on this system."); 1900 | 1901 | 1902 | read_var_names(prefices[mode], &names); 1903 | read_vars(names, &entry_list); 1904 | set_var_nums(prefices[mode], &entry_list); 1905 | 1906 | if (opts.delete) { 1907 | if (opts.num == -1 && opts.explicit_label == 0) { 1908 | errorx(3, 1909 | "You must specify an entry to delete (see the -b option or -L option)."); 1910 | } else { 1911 | if (opts.num != -1) { 1912 | ret = delete_var(prefices[mode], opts.num); 1913 | if (ret < 0) 1914 | error(15, "Could not delete variable"); 1915 | } else { 1916 | ret = delete_label(prefices[mode], opts.label); 1917 | if (ret < 0) 1918 | errorx(15, "Could not delete variable"); 1919 | } 1920 | } 1921 | } 1922 | 1923 | if (opts.active >= 0) { 1924 | if (opts.num == -1) { 1925 | errorx(4, 1926 | "You must specify a entry to activate (see the -b option)"); 1927 | } else { 1928 | ret = set_active_state(prefices[mode]); 1929 | if (ret < 0) 1930 | error(16, 1931 | "Could not set active state for %s%04X", 1932 | prefices[mode], opts.num); 1933 | } 1934 | } 1935 | 1936 | if (opts.reconnect >= 0) { 1937 | if (opts.num == -1) { 1938 | errorx(4, 1939 | "You must specify a driver entry to set re-connect on (see the -b option)"); 1940 | } else { 1941 | ret = set_force_reconnect(prefices[mode]); 1942 | if (ret < 0) 1943 | error(16, 1944 | "Could not set re-connect for %s%04X", 1945 | prefices[mode], opts.num); 1946 | } 1947 | } 1948 | 1949 | if (opts.create) { 1950 | warn_duplicate_name(&entry_list); 1951 | new_entry = make_var(prefices[mode], &entry_list); 1952 | if (!new_entry) 1953 | error(5, "Could not prepare %s variable", 1954 | prefices[mode]); 1955 | 1956 | /* Put this boot var in the right Order variable */ 1957 | if (new_entry && !opts.no_order) { 1958 | ret = add_to_order(order_name[mode], new_entry->num, 1959 | opts.index); 1960 | if (ret < 0) 1961 | error(6, "Could not add entry to %s", 1962 | order_name[mode]); 1963 | } 1964 | } else if (opts.index) { 1965 | error(1, "Index is meaningless without create"); 1966 | } 1967 | 1968 | if (opts.delete_order) { 1969 | ret = efi_del_variable(EFI_GLOBAL_GUID, order_name[mode]); 1970 | if (ret < 0 && errno != ENOENT) 1971 | error(7, "Could not remove entry from %s", 1972 | order_name[mode]); 1973 | } 1974 | 1975 | if (opts.order) { 1976 | ret = set_order(order_name[mode], prefices[mode], 1977 | opts.keep_old_entries); 1978 | if (ret < 0) 1979 | error(8, "Could not set %s", order_name[mode]); 1980 | } 1981 | 1982 | if (opts.deduplicate) { 1983 | ret = remove_dupes_from_order(order_name[mode]); 1984 | if (ret) 1985 | error(9, "Could not remove duplicates from %s order", 1986 | order_name[mode]); 1987 | } 1988 | 1989 | if (opts.delete_bootnext) { 1990 | ret = efi_del_variable(EFI_GLOBAL_GUID, "BootNext"); 1991 | if (ret < 0) 1992 | error(10, "Could not delete BootNext"); 1993 | } 1994 | 1995 | if (opts.delete_timeout) { 1996 | ret = efi_del_variable(EFI_GLOBAL_GUID, "Timeout"); 1997 | if (ret < 0) 1998 | error(11, "Could not delete Timeout"); 1999 | } 2000 | 2001 | if (opts.bootnext >= 0) { 2002 | if (!is_current_entry(opts.bootnext & 0xFFFF)) 2003 | errorx(12, "Boot entry %X does not exist", 2004 | opts.bootnext); 2005 | ret = set_u16("BootNext", opts.bootnext & 0xFFFF); 2006 | if (ret < 0) 2007 | error(13, "Could not set BootNext"); 2008 | } 2009 | 2010 | if (opts.set_timeout) { 2011 | ret = set_u16("Timeout", opts.timeout); 2012 | if (ret < 0) 2013 | error(14, "Could not set Timeout"); 2014 | } 2015 | 2016 | if (opts.set_mirror_lo || opts.set_mirror_hi) { 2017 | ret=set_mirror(opts.below4g, opts.above4g); 2018 | } 2019 | 2020 | if (!opts.quiet && ret == 0) { 2021 | switch (mode) { 2022 | case boot: 2023 | num = read_u16("BootNext"); 2024 | cond_warning(opts.verbose >= 2 && num < 0, 2025 | "Could not read variable 'BootNext'"); 2026 | if (num >= 0) 2027 | printf("BootNext: %04X\n", num); 2028 | num = read_u16("BootCurrent"); 2029 | cond_warning(opts.verbose >= 2 && num < 0, 2030 | "Could not read variable 'BootCurrent'"); 2031 | if (num >= 0) 2032 | printf("BootCurrent: %04X\n", num); 2033 | num = read_u16("Timeout"); 2034 | cond_warning(opts.verbose >= 2 && num < 0, 2035 | "Could not read variable 'Timeout'"); 2036 | if (num >= 0) 2037 | printf("Timeout: %u seconds\n", num); 2038 | show_order(order_name[mode]); 2039 | show_vars(prefices[mode]); 2040 | show_mirror(); 2041 | break; 2042 | case driver: 2043 | case sysprep: 2044 | show_order(order_name[mode]); 2045 | show_vars(prefices[mode]); 2046 | break; 2047 | } 2048 | } 2049 | free_vars(&entry_list); 2050 | free_array(names); 2051 | if (ret) 2052 | return 1; 2053 | return 0; 2054 | } 2055 | -------------------------------------------------------------------------------- /src/efibootmgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | efibootmgr.h - Manipulates EFI variables as exported in /proc/efi/vars 3 | 4 | Copyright (C) 2001 Dell Computer Corporation 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #pragma once 22 | 23 | #define EFIBOOTMGR_IPV4 0 24 | #define EFIBOOTMGR_IPV6 1 25 | 26 | #define EFIBOOTMGR_IPV4_ORIGIN_DHCP 0 27 | #define EFIBOOTMGR_IPV4_ORIGIN_STATIC 1 28 | #define EFIBOOTMGR_IPV6_ORIGIN_STATIC 0 29 | #define EFIBOOTMGR_IPV6_ORIGIN_STATELESS 1 30 | #define EFIBOOTMGR_IPV6_ORIGIN_STATEFUL 2 31 | 32 | #define EFIBOOTMGR_PATH_ABBREV_UNSPECIFIED 0 33 | #define EFIBOOTMGR_PATH_ABBREV_EDD10 1 34 | #define EFIBOOTMGR_PATH_ABBREV_HD 2 35 | #define EFIBOOTMGR_PATH_ABBREV_NONE 3 36 | #define EFIBOOTMGR_PATH_ABBREV_FILE 4 37 | 38 | typedef enum { 39 | boot, 40 | driver, 41 | sysprep, 42 | } ebm_mode; 43 | 44 | typedef struct { 45 | int argc; 46 | char **argv; 47 | int optind; 48 | char *disk; 49 | 50 | int ip_version; 51 | char *iface; 52 | char *macaddr; 53 | char *local_ip_addr; 54 | char *remote_ip_addr; 55 | char *gateway_ip_addr; 56 | char *ip_netmask; 57 | uint16_t ip_local_port; 58 | uint16_t ip_remote_port; 59 | uint16_t ip_protocol; 60 | uint8_t ip_addr_origin; 61 | 62 | char *loader; 63 | unsigned char *label; 64 | char *order; 65 | int keep_old_entries; 66 | char *testfile; 67 | char *extra_opts_file; 68 | uint32_t part; 69 | int abbreviate_path; 70 | uint32_t edd10_devicenum; 71 | int num; 72 | int bootnext; 73 | int verbose; 74 | int active; 75 | int reconnect; 76 | int below4g; 77 | int above4g; 78 | int deduplicate; 79 | unsigned int delete:1; 80 | unsigned int delete_order:1; 81 | unsigned int delete_bootnext:1; 82 | unsigned int quiet:1; 83 | unsigned int showversion:1; 84 | unsigned int create:1; 85 | unsigned int unicode:1; 86 | unsigned int forcegpt:1; 87 | unsigned int set_timeout:1; 88 | unsigned int delete_timeout:1; 89 | unsigned int set_mirror_lo:1; 90 | unsigned int set_mirror_hi:1; 91 | unsigned int no_order:1; 92 | unsigned int driver:1; 93 | unsigned int sysprep:1; 94 | unsigned int explicit_label:1; 95 | unsigned int list_supported_signature_types:1; 96 | short int timeout; 97 | uint16_t index; 98 | } efibootmgr_opt_t; 99 | 100 | extern efibootmgr_opt_t opts; 101 | -------------------------------------------------------------------------------- /src/efibootnext.c: -------------------------------------------------------------------------------- 1 | /* 2 | * efibootnext - Attempt to set a BootNext variable from existing boot 3 | * options. 4 | * 5 | * Copyright 2015 Red Hat, Inc. 6 | * Author: Peter Jones 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the Free 10 | * Software Foundation; either version 2 of the License, or (at your option) 11 | * any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 | * for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this library; if not, see . 20 | * 21 | */ 22 | 23 | #include "fix_coverity.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | typedef enum { 32 | notnot, // just a regular match sense 33 | ignore, // ignore this match - only consider its children 34 | not, // ! 35 | and, // -a 36 | or, // -o 37 | } match_sense; 38 | 39 | struct matcher { 40 | int sense; 41 | struct matcher *matchers; 42 | 43 | char *bootnum; 44 | char *disk; 45 | char *edd; 46 | char *edd_devnum; 47 | char *loader; 48 | char *label; 49 | int gpt; 50 | int in_boot_order; 51 | }; 52 | 53 | int 54 | main(int argc, char *argv[]) 55 | { 56 | int err_if_not_found = 1; 57 | int err_if_set_fails = 1; 58 | 59 | int which = 0; 60 | char *sorter = NULL; 61 | 62 | struct matcher *matchers; 63 | struct matcher *matcher; 64 | 65 | poptContext optCon; 66 | 67 | matcher = matchers = calloc(1, sizeof(struct matcher)); 68 | struct poptOption matchopts[] = { 69 | /* options to specify match criteria */ 70 | {"bootnum", 'n', POPT_ARG_STRING, matcher->bootnum, NULL, 71 | "boot entry number (hex)", "<####>" }, 72 | {"disk", 'd', POPT_ARG_STRING, matcher->disk, NULL, 73 | "disk containing loader", "" }, 74 | /* keep edd and device together despite alphabetism */ 75 | {"edd", 'e', POPT_ARG_STRING, matcher->edd, NULL, 76 | "EDD version", "[1|3|[any]]" }, 77 | {"device", 'E', POPT_ARG_STRING, matcher->edd_devnum, NULL, 78 | "EDD 1.0 device number (hex)", "[##|[80]]", }, 79 | /* keep gpt and mbr together despite alphabetism */ 80 | {"gpt", 'g', POPT_ARG_VAL, &matcher->gpt, 1, 81 | "only match GPT partitioned disks", }, 82 | {"mbr", 'm', POPT_ARG_VAL, &matcher->gpt, 2, 83 | "only match MBR partitioned disks", }, 84 | {"loader", 'l', POPT_ARG_STRING, matcher->loader, NULL, 85 | "loader path", "", }, 86 | {"label", 'L', POPT_ARG_STRING, matcher->label, NULL, 87 | "boot entry label", "