diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b64ae51 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,287 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "array-init" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6945cc5422176fc5e602e590c2878d2c2acd9a4fe20a4baa7c28022521698ec6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "binrw" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13d2fa8772b88ee64a0d84602c636ad2bae9c176873f6c440e2b0a1df4c8c43" +dependencies = [ + "array-init", + "binrw_derive", +] + +[[package]] +name = "binrw_derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193bcff2709da50edf2e9a89e0f9a2118eea70f257b1f4380ccf27d3d8578302" +dependencies = [ + "owo-colors", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "3.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + +[[package]] +name = "owo-colors" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rhexdump" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "uefivars" +version = "0.1.0" +dependencies = [ + "binrw", + "clap", + "lazy_static", + "rhexdump", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..046df83 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "uefivars" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +binrw = "0.8.4" +clap = { version = "3.1.18", features = ["derive"] } +lazy_static = "1.4.0" +rhexdump = "0.1.1" + diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..d838932 --- /dev/null +++ b/LICENCE @@ -0,0 +1,377 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bda77b --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# uefivars + +Utility for inspecting and managing a UEFI variable store firmware +volume, such as that used by The EDK II Project, and bhyve +in illumos. + +``` +% uefivars -l /zones/bloody/root/etc/uefivars +BOOT OPTIONS +------------ +Bootorder: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] +C H [0 ] UiApp - [App 462caa21-7614-4503-836e-8ab6f4662331] + [1 ] UEFI Misc Device - [PCI 4.0] + [2 ] UEFI Misc Device 2 - [PCI 5.0] + [3 ] UEFI Misc Device 3 - [PCI 5.1] + [4 ] UEFI Misc Device 4 - [PCI 5.2] + [5 ] UEFI PXEv4 (MAC:020820101CB6) - [PCI 6.0] + [6 ] UEFI HTTPv4 (MAC:020820101CB6) - [PCI 6.0] [HTTP] + [7 ] UEFI PXEv4 (MAC:0208202240BF) - [PCI 6.2] + [8 ] UEFI HTTPv4 (MAC:0208202240BF) - [PCI 6.2] [HTTP] + [9 ] UEFI PXEv4 (MAC:0208208D7837) - [PCI 6.3] + [10] UEFI HTTPv4 (MAC:0208208D7837) - [PCI 6.3] [HTTP] + [11] EFI Internal Shell - [App 7c04a583-9e3e-4f1c-ad65-e05268d0b4d1] +C - Current (first in boot order) + N - Next Boot + H - Hidden +``` + diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..ecd0b6d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +max_width = 80 +use_small_heuristics = "max" +#nightly struct_field_align_threshold = 20 +edition = "2021" diff --git a/src/efi.rs b/src/efi.rs new file mode 100644 index 0000000..ff7b3fa --- /dev/null +++ b/src/efi.rs @@ -0,0 +1,865 @@ +use std::fmt; +use std::cmp::min; +use std::collections::{HashMap, HashSet}; +use std::str::FromStr; +use std::num::ParseIntError; + +use binrw::{binread, binrw, BinReaderExt}; +use binrw::helpers::{until, until_exclusive, until_eof}; +use binrw::io::{Cursor, SeekFrom}; + +// The uefi-edk2 nvram firmware volume is divided into sections as follows: +// +// 0x00000 - 0x0dfff NV_VARIABLE_STORE (Firmware volume) +// 0x0e000 - 0x0efff NV_EVENT_LOG +// 0x0f000 - 0x0ffff NV_FTW_WORKING (Fault-tolerant-write) +// 0x10000 - 0x1ffff NV_FTW_SPARE +// +// This is valid for firmware generated with an FD_SIZE of 1024 or 2048 +// I have yet to see anything in the event log area, but it is preserved +// when rewriting. + +const NV_EVENT_LOG: u64 = 0xe000; +const NV_EVENT_LOG_LEN: u64 = 0x1000; +const NV_FTW_WORKING: u64 = 0xf000; +//const NV_FTW_SPARE: u64 = 0x10000; + +const EFI_SYSTEM_NV_DATA_FV_GUID: EfiGuid = EfiGuid { + data1: 0xfff1_2b8d, + data2: 0x7696, + data3: 0x4c8b, + data4: [0xa9, 0x85, 0x27, 0x47, 0x7, 0x5b, 0x4f, 0x50], +}; + +const EFI_AUTHENTICATED_VARIABLE_GUID: EfiGuid = EfiGuid { + data1: 0xaaf3_2c78, + data2: 0x947b, + data3: 0x439a, + data4: [0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92], +}; + +const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER: EfiGuid = EfiGuid { + data1: 0x9e58292b, + data2: 0x7c68, + data3: 0x497d, + data4: [ 0xa0, 0xce, 0x65, 0x0, 0xfd, 0x9f, 0x1b, 0x95 ], +}; + +pub const EFI_GLOBAL_VARIABLE_GUID: EfiGuid = EfiGuid { + data1: 0x8be4_df61, + data2: 0x93ca, + data3: 0x11d2, + data4: [0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c], +}; + +const VARIABLE_DATA: u16 = 0x55aa; + +const VAR_STORE_FORMATTED: u8 = 0x5a; +const VAR_STORE_HEALTHY: u8 = 0xfe; + +pub const VAR_ADDED: u8 = 0x3f; +const VAR_DELETED: u8 = 0xfd; +const VAR_IN_DELETED_TRANSITION: u8 = 0xfe; +const VAR_HEADER_VALID_ONLY: u8 = 0x7f; +const VAR_ADDED_TRANSITION: u8 = VAR_ADDED & VAR_IN_DELETED_TRANSITION; +const VAR_DELETED_TRANSITION: u8 = + VAR_ADDED & VAR_DELETED & VAR_IN_DELETED_TRANSITION; + +#[binrw] +#[allow(dead_code)] +#[derive(Debug)] +pub struct Volume { + #[br(assert(zero_vector == 0))] + zero_vector: u128, + #[br(assert(guid == EFI_SYSTEM_NV_DATA_FV_GUID, + "Unexpected GUID in file, got {:#x?}", guid))] + guid: EfiGuid, + pub volsize: u64, + #[br(assert(signature == 0x4856465f))] // "_FVH" + signature: u32, + attributes: u32, + headerlen: u16, + checksum: u16, + #[br(assert(ext_hdr_offset == 0))] // Cannot handle extended headers + ext_hdr_offset: u16, + #[br(assert(_rsvd1 == 0))] + _rsvd1: u8, + #[br(assert(revision == 2))] + revision: u8, + #[br(parse_with = until(|&b: &BlockMapEntry| + b.num == 0 && b.len == 0))] + maps: Vec, + // Is this a bug in binrw? When using 'until', the cursor ends up one block + // too far on so it needs to be rewinded by sizeof (BlockMapEntry). + #[br(seek_before(SeekFrom::Current(-8)))] + header: VariableStoreHeader, + #[br(parse_with = until_exclusive(|v: &AuthVariable| + v.startid != VARIABLE_DATA + ))] + pub vars: Vec, + + // There is a gap here, which is filled with 0xff in the original + // (possibly emulating erased flash). + // When it's written back, it ends up being zeroed. + + #[brw(seek_before(SeekFrom::Start(NV_EVENT_LOG)))] + #[br(count = NV_EVENT_LOG_LEN)] + eventlog: Vec, + + // TBD: Always replace this with a clean block, or preserve? + #[brw(seek_before(SeekFrom::Start(NV_FTW_WORKING)))] + ftw: FTWBlockHeader, + #[br(parse_with = until_eof)] + ftwdata: Vec, +} + +#[binrw] +#[allow(dead_code)] +#[derive(Debug)] +pub struct VariableStoreHeader { + // uefi-edk2 ships an authenticated variable store + #[br(assert(guid == EFI_AUTHENTICATED_VARIABLE_GUID, + "Unexpected store header GUID, got {:#x?}", guid))] + guid: EfiGuid, + size: u32, + #[br(assert(format == VAR_STORE_FORMATTED))] + format: u8, + #[br(assert(state == VAR_STORE_HEALTHY))] + state: u8, + _rsvd1: u16, + _rsvd2: u32, +} + +#[binrw] +#[allow(dead_code)] +#[derive(Debug)] +pub struct FTWBlockHeader { + #[br(assert(guid == EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, + "Unexpected FTW header GUID, got {:#x?}", guid))] + guid: EfiGuid, + crc32: u32, + flags: u32, + queuesize: u64, +} + +#[binrw] +#[allow(dead_code)] +#[derive(Debug)] +pub struct AuthVariable { + pub startid: u16, + pub state: u8, + pub _rsvd1: u8, + pub attributes: u32, + pub count: u64, + pub timestamp: EfiTime, + pub pubkeyindex: u32, + pub namelen: u32, + pub datalen: u32, + pub guid: EfiGuid, + #[br(if(startid == VARIABLE_DATA))] + #[br(parse_with = utf16::read, args((namelen)))] + #[bw(write_with = utf16::write)] + pub name: String, + #[br(if(startid == VARIABLE_DATA))] + #[br(count = datalen)] + #[brw(align_after = 4)] + pub data: Vec, +} + +const EFI_VARIABLE_NON_VOLATILE: u32 = 0x00000001; +const EFI_VARIABLE_BOOTSERVICE_ACCESS: u32 = 0x00000002; +const EFI_VARIABLE_RUNTIME_ACCESS: u32 = 0x00000004; +const EFI_VARIABLE_HARDWARE_ERROR_RECORD: u32 = 0x00000008; +const EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS: u32 = 0x00000020; +const EFI_VARIABLE_APPEND_WRITE: u32 = 0x00000040; + +impl fmt::Display for AuthVariable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let m = min(self.data.len(), 7); + let guid = match resolve_guid(&self.guid) { + Some(s) => s.to_string(), + None => self.guid.to_string(), + }; + let mut attrlist = Vec::new(); + + if self.attributes & EFI_VARIABLE_NON_VOLATILE != 0 { + attrlist.push("NV"); + } + + if self.attributes & EFI_VARIABLE_RUNTIME_ACCESS != 0 { + attrlist.push("RT"); + attrlist.push("BS"); + } else if self.attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS != 0 { + attrlist.push("BS"); + } + + if self.attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD != 0 { + attrlist.push("HR"); + } + + if self.attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS != 0 { + attrlist.push("AT"); + } + + if self.attributes & EFI_VARIABLE_APPEND_WRITE != 0 { + attrlist.push("AW"); + } + + let statestr = format!("{:02x}?", self.state); + + let state = match self.state { + VAR_ADDED => " ", + VAR_ADDED_TRANSITION => "ADT", + VAR_DELETED => "DEL", + VAR_DELETED_TRANSITION => "DEL", + VAR_HEADER_VALID_ONLY => "HVO", + _ => &statestr, + }; + + write!(f, "{:^40} {} {:^10} {} {:?}{}", + guid, state, attrlist.join("+"), self.name, + &self.data[..m], + if self.data.len() > m { + format!("..{}", self.data.len() - 1) + } else { + "".to_string() + } + ) + } +} + +impl Default for AuthVariable { + fn default() -> Self { + Self { + startid: VARIABLE_DATA, + state: VAR_ADDED, + _rsvd1: 0, + attributes: EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_RUNTIME_ACCESS, + count: 0, + timestamp: EfiTime::default(), + pubkeyindex: 0, + namelen: 0, + datalen: 0, + guid: EFI_GLOBAL_VARIABLE_GUID.clone(), + name: "".to_string(), + data: Vec::new(), + } + } +} + +impl Volume { + + pub fn boot_entries(&self) -> BootEntryIter { + BootEntryIter { volume: self, slot: None } + } + + pub fn defrag(&mut self) { + // To de-fragment a variables file, one needs to: + // - preserve all VAR_ADDED variables + // - promote VAR_IN_DELETED_TRANSITION variables to VAR_ADDED providing + // there is not already a VAR_ADDED variable with the same GUID and + // name. + // - remove anything remaining that is not VAR_ADDED. + + // Build a list of VAR_ADDED + let mut known: HashSet = HashSet::new(); + for v in &self.vars { + if v.state == VAR_ADDED { + known.insert(format!("{}/{}", v.guid, v.name).to_string()); + } + } + + self.vars.retain(|v| { + v.state == VAR_ADDED || ( + v.state == (VAR_ADDED & VAR_IN_DELETED_TRANSITION) && + !known.contains(&format!("{}/{}", v.guid, v.name).to_string())) + }); + + // Now promote any remaining ADDED/IN_DELETED_TRANSITION entries + for v in &mut self.vars { + v.state = VAR_ADDED; + } + } + + pub fn remove_var(&mut self, name: &str, guid: &str) { + self.vars.retain(|v| { + v.name != name || v.guid.to_string() != guid + }); + } + + pub fn find_var(&self, name: &str, guid: &str) -> Option<&AuthVariable> { + for v in &self.vars { + if v.state == VAR_ADDED && v.name == name && + v.guid.to_string() == guid { + return Some(v); + } + } + None + } + + pub fn boot_order(&self) -> Option { + if let Some(cv) = self.find_var("BootOrder", + &EFI_GLOBAL_VARIABLE_GUID.to_string()) { + if cv.datalen > 1 { + let mut c = Cursor::new(&cv.data); + let bo: BootOrder = c.read_le().unwrap(); + return Some(bo) + } + } + None + } + + pub fn boot_next(&self) -> Option { + if let Some(nv) = self.find_var("BootNext", + &EFI_GLOBAL_VARIABLE_GUID.to_string()) { + if nv.datalen > 1 { + return Some( + (nv.data[0] as u16) | ((nv.data[1] as u16) << 8) + ); + } + } + None + } +} + +#[binrw] +#[derive(PartialEq, Debug, Clone, Default)] +pub struct EfiGuid { + data1: u32, + data2: u16, + data3: u16, + data4: [u8; 8], +} + +impl fmt::Display for EfiGuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:08x}-{:04x}-{:04x}-{:02x}{:02x}-\ + {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + self.data1, self.data2, self.data3, self.data4[0], self.data4[1], + self.data4[2], self.data4[3], self.data4[4], self.data4[5], + self.data4[6], self.data4[7]) + } +} + +impl FromStr for EfiGuid { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('-').collect(); + if parts.len() != 5 { + panic!("Invalid number of parts in guid"); + } + let mut guid = EfiGuid::default(); + guid.data1 = u32::from_str_radix(&parts[0], 16)?; + guid.data2 = u16::from_str_radix(&parts[1], 16)?; + guid.data3 = u16::from_str_radix(&parts[2], 16)?; + guid.data4[0] = u8::from_str_radix(&parts[3][..2], 16)?; + guid.data4[1] = u8::from_str_radix(&parts[3][2..], 16)?; + let mut i = 2; + for b in parts[4].as_bytes().chunks(2) { + let s = std::str::from_utf8(b).unwrap(); + guid.data4[i] = u8::from_str_radix(s, 16)?; + i += 1 + } + Ok(guid) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::num::IntErrorKind; + + #[test] + fn guid_to_str() { + assert_eq!(EFI_GLOBAL_VARIABLE_GUID.to_string(), + "8be4df61-93ca-11d2-aa0d-00e098032b8c" + ); + } + + #[test] + fn guid_from_str() { + assert_eq!( + EfiGuid::from_str("8be4df61-93ca-11d2-aa0d-00e098032b8c"), + Ok(EFI_GLOBAL_VARIABLE_GUID) + ); + } + + #[test] + fn guid_from_str_err() { + let err = EfiGuid::from_str("xbe4df61-93ca-11d2-aa0d-00e098032b8c") + .unwrap_err(); + let kind = err.kind(); + assert_eq!(kind, &IntErrorKind::InvalidDigit); + let err = EfiGuid::from_str("018be4df61-93ca-11d2-aa0d-00e098032b8c") + .unwrap_err(); + let kind = err.kind(); + assert_eq!(kind, &IntErrorKind::PosOverflow); + } +} + +#[binrw] +#[allow(dead_code)] +#[derive(Debug, Default)] +pub struct EfiTime { + year: u16, + month: u8, + day: u8, + hour: u8, + min: u8, + sec: u8, + _pad1: u8, + nanosec: u32, + tz: u16, + daylight: u8, + _pad2: u8, +} + +impl fmt::Display for EfiTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{}", + self.year, self.month, self.day, + self.hour, self.min, self.sec, + self.nanosec) + } +} + +#[binrw] +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub struct BlockMapEntry { + num: u32, + len: u32, +} + +#[binread] +#[allow(dead_code)] +pub struct BootOrder { + #[br(restore_position)] + pub first: u16, + #[br(parse_with = until_eof)] + pub order: Vec, +} + +// See section 3.1.3 of the UEFI specification, version 2.9, for the +// EFI_LOAD_OPTION structure. +#[binread] +#[allow(dead_code)] +#[derive(Debug)] +pub struct BootEntry { + #[br(ignore)] + pub slot: u16, + #[br(ignore)] + pub name: String, + #[br(ignore)] + pub title: String, + + pub attributes: u32, + fplength: u16, + #[br(parse_with = until_exclusive(|v| *v == 0))] + pub rawtitle: Vec, + + #[br(parse_with = + until_exclusive(|v: &DevicePath| + v.device_type == 0x7f && v.sub_type == 0xff))] + pub pathlist: Vec, + #[br(parse_with = until_eof)] + pub optionaldata: Vec, + + #[br(ignore)] + pub btype: BootEntryType, + + #[br(ignore)] + pub uri: bool +} + +pub enum BootEntryType { + Unknown, + PCI(u8, u8), + App(EfiGuid), +} + +impl Default for BootEntryType { + fn default() -> Self { + BootEntryType::Unknown + } +} + +impl fmt::Debug for BootEntryType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", match self { + BootEntryType::PCI(f, d) => format!("PCI {}.{}", d, f).to_string(), + BootEntryType::App(guid) => format!("App {}", guid).to_string(), + BootEntryType::Unknown => "unknown".to_string(), + }) + } +} + +// Section 10.3.1 - Generic Device Path Structures +#[binread] +#[allow(dead_code)] +#[derive(Debug)] +pub struct DevicePath { + // header + pub device_type: u8, + pub sub_type: u8, + pub length: u16, + #[br(count = length - 4)] + pub data: Vec, +} + +#[allow(dead_code)] +pub enum DeviceType { + HardwareDevicePath = 1, + ACPIDevicePath = 2, + MessagingDevicePath = 3, + MediaDevicePath = 4, + BIOSDevicePath = 5, + EOD = 0x7f, +} + +pub const LOAD_OPTION_HIDDEN: u32 = 0x8; + +pub struct BootEntryIter<'v> { + slot: Option, + volume: &'v Volume, +} + +impl Iterator for BootEntryIter<'_> { + type Item = BootEntry; + + fn next(&mut self) -> Option { + let next: u16; + match self.slot { + None => next = 0, + Some(u16::MAX) => return None, + Some(x) => next = x + 1, + }; + self.slot = Some(next); + let var = format!("Boot{:04X}", next); + if let Some(entry) = self.volume.find_var(&var, + &EFI_GLOBAL_VARIABLE_GUID.to_string()) { + + let mut c = Cursor::new(&entry.data); + let mut elo: Self::Item = c.read_le().unwrap(); + + elo.slot = next; + elo.name = var; + elo.title = String::from_utf16_lossy(elo.rawtitle.as_slice()); + + for p in &elo.pathlist { + if p.device_type == 1 && p.sub_type == 1 && p.data.len() == 2 { + elo.btype = BootEntryType::PCI(p.data[0], p.data[1]); + } + if p.device_type == 4 && p.sub_type == 6 && + p.data.len() == 16 { + + let mut pc = Cursor::new(&p.data); + let guid: EfiGuid = pc.read_le().unwrap(); + elo.btype = BootEntryType::App(guid); +// 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid + } + if p.device_type == 3 && p.sub_type == 24 { + elo.uri = true; + } + } + + Some(elo) + } else { + self.slot = Some(u16::MAX); // fuse + None + } + } +} + +mod utf16 { + use binrw::io::prelude::*; + use binrw::prelude::*; + use binrw::ReadOptions; + use binrw::WriteOptions; + use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; + + pub fn read (reader: &mut R, _ro: &ReadOptions, args: (u32,)) + -> BinResult + where R: Read + Seek + { + let mut i = args.0 as usize; + let mut v: Vec = Vec::with_capacity(i / 2); + + while i > 0 { + let ch: u16 = reader.read_le().unwrap(); + if ch != 0 { + v.push(ch); + } + i -= 2 + } + let s = decode_utf16(v.iter().cloned()) + .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) + .collect::(); + + Ok(s) + } + + pub fn write (data: &String, writer: &mut W, wo: &WriteOptions, _: ()) + -> binrw::BinResult<()> + where W: binrw::io::Write + binrw::io::Seek + { + let mut v: Vec = data.encode_utf16().collect(); + v.push(0); + + v.write_options(writer, wo, ())?; + Ok(()) + } +} + +pub fn resolve_guid(guid: &EfiGuid) -> Option<&str> { + KNOWN_GUIDS.get(guid.to_string().as_str()).cloned() +} + +lazy_static! { + static ref KNOWN_GUIDS: HashMap<&'static str, &'static str> = { + let mut m = HashMap::new(); + // Ones I've seen in use with bhyve guests + m.insert("8be4df61-93ca-11d2-aa0d-00e098032b8c", + "GLOBAL_VARIABLE"); + m.insert("04b37fe8-f6ae-480b-bdd5-37d98c5e89aa", + "EDKII_VAR_ERROR_FLAG"); + m.insert("59324945-ec44-4c0d-b1cd-9db139df070c", + "EFI_ISCSI_INITIATOR_NAME_PROTOCOL_GUID"); + m.insert("5b446ed1-e30b-4faa-871a-3654eca36080", + "EFI_IP4_CONFIG2_PROTOCOL_GUID"); + m.insert("4c19049f-4137-4dd3-9c10-8b97a83ffdfa", + "EFI_MEMORY_TYPE_INFORMATION_GUID"); + m.insert("fab7e9e1-39dd-4f2b-8408-e20e906cb6de", + "HD_BOOT_DEVICE_PATH_VARIABLE"); + m.insert("eb704011-1402-11d3-8e77-00a0c969723b", + "MTC_VENDOR"); + m.insert("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9", + "ISCSI_CONFIG"); + m.insert("77fa9abd-0359-4d32-bd60-28f4e78f784b", + "SecureBootPlatformID"); + m.insert("eaec226f-c9a3-477a-a826-ddc716cdc0e3", + "WindowsID"); + + // Others + m.insert("00720665-67eb-4a99-baf7-d3c33a1c7cc9", + "EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("02e800be-8f01-4aa6-946b-d71388e1833f", + "EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("0379be4e-d706-437d-b037-edb82fb772a4", + "EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID"); + m.insert("03c4e603-ac28-11d3-9a2d-0090273fc14d", + "EFI_PXE_BASE_CODE_PROTOCOL_GUID"); + m.insert("05ad34ba-6f02-4214-952e-4da0398e2bb9", + "EFI_DXE_SERVICES_TABLE_GUID"); + m.insert("09576e91-6d3f-11d2-8e39-00a0c969723b", + "EFI_DEVICE_PATH_PROTOCOL_GUID"); + m.insert("09576e92-6d3f-11d2-8e39-00a0c969723b", + "EFI_FILE_INFO_ID"); + m.insert("09576e93-6d3f-11d2-8e39-00a0c969723b", + "EFI_FILE_SYSTEM_INFO_ID"); + m.insert("0db48a36-4e54-ea9c-9b09-1ea5be3a660b", + "EFI_REST_PROTOCOL_GUID"); + m.insert("0faaecb1-226e-4782-aace-7db9bcbf4daf", + "EFI_FTP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("107a772c-d5e1-11d4-9a46-0090273fc14d", + "EFI_COMPONENT_NAME_PROTOCOL_GUID"); + m.insert("11b34006-d85b-4d0a-a290-d5a571310ef7", + "PCD_PROTOCOL_GUID"); + m.insert("13a3f0f6-264a-3ef0-f2e0-dec512342f34", + "EFI_PCD_PROTOCOL_GUID"); + m.insert("13ac6dd1-73d0-11d4-b06b-00aa00bd6de7", + "EFI_EBC_INTERPRETER_PROTOCOL_GUID"); + m.insert("143b7632-b81b-4cb7-abd3-b625a5b9bffe", + "EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID"); + m.insert("151c8eae-7f2c-472c-9e54-9828194f6a88", + "EFI_DISK_IO2_PROTOCOL_GUID"); + m.insert("1682fe44-bd7a-4407-b7c7-dca37ca3922d", + "EFI_TLS_CONFIGURATION_PROTOCOL_GUID"); + m.insert("18a031ab-b443-4d1a-a5c0-0c09261e9f71", + "EFI_DRIVER_BINDING_PROTOCOL_GUID"); + m.insert("1c0c34f6-d380-41fa-a049-8ad06c1a66aa", + "EFI_EDID_DISCOVERED_PROTOCOL_GUID"); + m.insert("1d3de7f0-0807-424f-aa69-11a54e19a46f", + "EFI_ATA_PASS_THRU_PROTOCOL_GUID"); + m.insert("2755590c-6f3c-42fa-9ea4-a3ba543cda25", + "EFI_DEBUG_SUPPORT_PROTOCOL_GUID"); + m.insert("2a534210-9280-41d8-ae79-cada01a2b127", + "EFI_DRIVER_HEALTH_PROTOCOL_GUID"); + m.insert("2c8759d5-5c2d-66ef-925f-b66c101957e2", + "EFI_IP6_PROTOCOL_GUID"); + m.insert("2f707ebb-4a1a-11d4-9a38-0090273fc14d", + "EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID"); + m.insert("31878c87-0b75-11d5-9a4f-0090273fc14d", + "EFI_SIMPLE_POINTER_PROTOCOL_GUID"); + m.insert("31a6406a-6bdf-4e46-b2a2-ebaa89c40920", + "EFI_HII_IMAGE_PROTOCOL_GUID"); + m.insert("330d4706-f2a0-4e4f-a369-b66fa8d54385", + "EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID"); + m.insert("387477c1-69c7-11d2-8e39-00a0c969723b", + "EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID"); + m.insert("387477c2-69c7-11d2-8e39-00a0c969723b", + "EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID"); + m.insert("39b68c46-f7fb-441b-b6ec-16b0f69821f3", + "EFI_CAPSULE_REPORT_GUID"); + m.insert("3ad9df29-4501-478d-b1f8-7f7fe70e50f3", + "EFI_UDP4_PROTOCOL_GUID"); + m.insert("3b95aa31-3793-434b-8667-c8070892e05e", + "EFI_IP4_CONFIG_PROTOCOL_GUID"); + m.insert("3e35c163-4074-45dd-431e-23989dd86b32", + "EFI_HTTP_UTILITIES_PROTOCOL_GUID"); + m.insert("3e745226-9818-45b6-a2ac-d7cd0e8ba2bc", + "EFI_USB2_HC_PROTOCOL_GUID"); + m.insert("41d94cd2-35b6-455a-8258-d4e51334aadd", + "EFI_IP4_PROTOCOL_GUID"); + m.insert("49152e77-1ada-4764-b7a2-7afefed95e8b", + "EFI_DEBUG_IMAGE_INFO_TABLE_GUID"); + m.insert("4cf5b200-68b8-4ca5-9eec-b23e3f50029a", + "EFI_PCI_IO_PROTOCOL_GUID"); + m.insert("4d330321-025f-4aac-90d8-5ed900173b63", + "EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID"); + m.insert("4f948815-b4b9-43cb-8a33-90e060b34955", + "EFI_UDP6_PROTOCOL_GUID"); + m.insert("587e72d7-cc50-4f79-8209-ca291fc1a10f", + "EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID"); + m.insert("5b1b31a1-9562-11d2-8e3f-00a0c969723b", + "EFI_LOADED_IMAGE_PROTOCOL_GUID"); + m.insert("5c198761-16a8-4e69-972c-89d67954f81d", + "EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL_GUID"); + m.insert("65530bc7-a359-410f-b010-5aadc7ec2b62", + "EFI_TCP4_PROTOCOL_GUID"); + m.insert("66ed4721-3c98-4d3e-81e3-d03dd39a7254", + "EFI_UDP6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("6a1ee763-d47a-43b4-aabe-ef1de2ab56fc", + "EFI_HII_PACKAGE_LIST_PROTOCOL_GUID"); + m.insert("6a7a5cff-e8d9-4f70-bada-75ab3025ce14", + "EFI_COMPONENT_NAME2_PROTOCOL_GUID"); + m.insert("6b30c738-a391-11d4-9a3b-0090273fc14d", + "EFI_PLATFORM_DRIVER_OVERRIDE_PROTOCOL_GUID"); + m.insert("7739f24c-93d7-11d4-9a3a-0090273fc14d", + "EFI_HOB_LIST_GUID"); + m.insert("78247c57-63db-4708-99c2-a8b4a9a61f6b", + "EFI_MTFTP4_PROTOCOL_GUID"); + m.insert("783658a3-4172-4421-a299-e009079c0cb4", + "EFI_LEGACY_BIOS_PLATFORM_PROTOCOL_GUID"); + m.insert("7a59b29b-910b-4171-8242-a85a0df25b5b", + "EFI_HTTP_PROTOCOL_GUID"); + m.insert("7ab33a91-ace5-4326-b572-e7ee33d39f16", + "EFI_MANAGED_NETWORK_PROTOCOL_GUID"); + m.insert("7f1647c8-b76e-44b2-a565-f70ff19cd19e", + "EFI_DNS6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("83f01464-99bd-45e5-b383-af6305d8e9e6", + "EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("87c8bad7-0595-4053-8297-dede395f5d5b", + "EFI_DHCP6_PROTOCOL_GUID"); + m.insert("8868e871-e4f1-11d3-bc22-0080c73c8881", + "EFI_ACPI_TABLE_GUID"); + m.insert("8a219718-4ef5-4761-91c8-c0f04bda9e56", + "EFI_DHCP4_PROTOCOL_GUID"); + m.insert("8b843e20-8132-4852-90cc-551a4e4a7f1c", + "EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID"); + m.insert("8d59d32b-c655-4ae9-9b15-f25904992a43", + "EFI_ABSOLUTE_POINTER_PROTOCOL_GUID"); + m.insert("9042a9de-23dc-4a38-96fb-7aded080516a", + "EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID"); + m.insert("937fe521-95ae-4d1a-8929-48bcd90ad31a", + "EFI_IP6_CONFIG_PROTOCOL_GUID"); + m.insert("952cb795-ff36-48cf-a249-4df486d6ab8d", + "EFI_TLS_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("964e5b21-6459-11d2-8e39-00a0c969723b", + "EFI_BLOCK_IO_PROTOCOL_GUID"); + m.insert("964e5b22-6459-11d2-8e39-00a0c969723b", + "EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID"); + m.insert("9d9a39d8-bd42-4a73-a4d5-8ee94be11380", + "EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("9e23d768-d2f3-4366-9fc3-3a7aba864374", + "EFI_VLAN_CONFIG_PROTOCOL_GUID"); + m.insert("9fb9a8a1-2f4a-43a6-889c-d0f7b6c47ad5", + "EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("a3979e64-ace8-4ddc-bc07-4d66b8fd0977", + "EFI_IPSEC2_PROTOCOL_GUID"); + m.insert("a4c751fc-23ae-4c3e-92e9-4964cf63f349", + "EFI_UNICODE_COLLATION_PROTOCOL2_GUID"); + m.insert("a77b2472-e282-4e9f-a245-c2c0e27bbcc1", + "EFI_BLOCK_IO2_PROTOCOL_GUID"); + m.insert("ae3d28cc-e05b-4fa1-a011-7eb55a3f1401", + "EFI_DNS4_PROTOCOL_GUID"); + m.insert("b625b186-e063-44f7-8905-6a74dc6f52b4", + "EFI_DNS4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("b9d4c360-bcfb-4f9b-9298-53c136982258", + "EFI_FORM_BROWSER2_PROTOCOL_GUID"); + m.insert("bb25cf6f-f1d4-11d2-9a0c-0090273fc1fd", + "EFI_SERIAL_IO_PROTOCOL_GUID"); + m.insert("bc62157e-3e33-4fec-9920-2d3b36d750df", + "EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID"); + m.insert("bd8c1056-9f36-44ec-92a8-a6337f817986", + "EFI_EDID_ACTIVE_PROTOCOL_GUID"); + m.insert("bdc8e6af-d9bc-4379-a72a-e0c4e75dae1c", + "EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("bf0a78ba-ec29-49cf-a1c9-7ae54eab6a51", + "EFI_MTFTP6_PROTOCOL_GUID"); + m.insert("c51711e7-b4bf-404a-bfb8-0a048ef1ffe4", + "EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("c68ed8e2-9dc6-4cbd-9d94-db65acc5c332", + "EFI_SMM_COMMUNICATION_PROTOCOL_GUID"); + m.insert("ca37bc1f-a327-4ae9-828a-8c40d8506a17", + "EFI_DNS6_PROTOCOL_GUID"); + m.insert("ce345171-ba0b-11d2-8e4f-00a0c969723b", + "EFI_DISK_IO_PROTOCOL_GUID"); + m.insert("ce5e5929-c7a3-4602-ad9e-c9daf94ebfcf", + "EFI_IPSEC_CONFIG_PROTOCOL_GUID"); + m.insert("d42ae6bd-1352-4bfb-909a-ca72a6eae889", + "LZMAF86_CUSTOM_DECOMPRESS_GUID"); + m.insert("d8117cfe-94a6-11d4-9a3a-0090273fc14d", + "EFI_DECOMPRESS_PROTOCOL_GUID"); + m.insert("d9760ff3-3cca-4267-80f9-7527fafa4223", + "EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("db47d7d3-fe81-11d3-9a35-0090273fc14d", + "EFI_FILE_SYSTEM_VOLUME_LABEL_ID"); + m.insert("dd9e7534-7762-4698-8c14-f58517a625aa", + "EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID"); + m.insert("dfb386f7-e100-43ad-9c9a-ed90d08a5e12", + "EFI_IPSEC_PROTOCOL_GUID"); + m.insert("e9ca4775-8657-47fc-97e7-7ed65a084324", + "EFI_HII_FONT_PROTOCOL_GUID"); + m.insert("eb338826-681b-4295-b356-2b364c757b09", + "EFI_FTP4_PROTOCOL_GUID"); + m.insert("eb9d2d2f-2d88-11d3-9a16-0090273fc14d", + "MPS_TABLE_GUID"); + m.insert("eb9d2d30-2d88-11d3-9a16-0090273fc14d", + "ACPI_TABLE_GUID"); + m.insert("eb9d2d31-2d88-11d3-9a16-0090273fc14d", + "SMBIOS_TABLE_GUID"); + m.insert("eb9d2d32-2d88-11d3-9a16-0090273fc14d", + "SAL_SYSTEM_TABLE_GUID"); + m.insert("eba4e8d2-3858-41ec-a281-2647ba9660d0", + "EFI_DEBUGPORT_PROTOCOL_GUID"); + m.insert("ec20eb79-6c1a-4664-9a0d-d2e4cc16d664", + "EFI_TCP6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("ec835dd3-fe0f-617b-a621-b350c3e13388", + "EFI_IP6_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("ee4e5898-3914-4259-9d6e-dc7bd79403cf", + "LZMA_CUSTOM_DECOMPRESS_GUID"); + m.insert("ef9fc172-a1b2-4693-b327-6d32fc416042", + "EFI_HII_DATABASE_PROTOCOL_GUID"); + m.insert("f2fd1544-9794-4a2c-992e-e5bbcf20e394", + "SMBIOS3_TABLE_GUID"); + m.insert("f36ff770-a7e1-42cf-9ed2-56f0f271f44c", + "EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("f44c00ee-1f2c-4a00-aa09-1c9f3e0800a3", + "EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID"); + m.insert("f4b427bb-ba21-4f16-bc4e-43e416ab619c", + "EFI_ARP_PROTOCOL_GUID"); + m.insert("f4ccbfb7-f6e0-47fd-9dd4-10a8f150c191", + "EFI_SMM_BASE2_PROTOCOL_GUID"); + m.insert("f541796d-a62e-4954-a775-9584f61b9cdd", + "EFI_TCG_PROTOCOL_GUID"); + m.insert("fc1bcdb0-7d31-49aa-936a-a4600d9dd083", + "EFI_CRC32_GUIDED_SECTION_EXTRACTION_GUID"); + m.insert("ffe06bdd-6107-46a6-7bb2-5a9c7ec5275c", + "EFI_ACPI_TABLE_PROTOCOL_GUID"); + m + }; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5dd75a4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,226 @@ +use std::fs::File; +use std::fs::OpenOptions; +use std::path::PathBuf; +//use std::io::Write; + +use binrw::prelude::*; +use clap::Parser; + +#[macro_use] +extern crate lazy_static; + +use rhexdump; + +mod efi; + +#[derive(Parser, Debug)] +#[clap(version)] +struct Args { + /// Output file + #[clap(short, long, parse(from_os_str), value_name = "file")] + output: Option, + + /// Increase verbosity + #[clap(short, long)] + verbose: bool, + + /// Show all variables (included deleted ones) + #[clap(short, long)] + all: bool, + + /// Produce debugging output + #[clap(short, long)] + debug: bool, + + /// De-fragment the file + #[clap(short='D', long)] + defrag: bool, + + /// Select the boot entry for the next boot + #[clap(short, long, value_name = "id")] + bootnext: Option, + + /// List available boot options + #[clap(short, long)] + list: bool, + + /// Show only variables containing + #[clap(short, long, value_name = "substr")] + filter: Option, + + /// A UEFI variable firmware volume file + #[clap(required(true), value_name = "input", + parse(from_os_str))] + file: Option, +} + +fn main() { + let args = Args::parse(); + + // Is there a way to manage this via clap's derive interface? + match args.output { + None => { + if args.defrag || args.bootnext != None { + println!("An output file name must also be provided."); + std::process::exit(1); + } + } + _ => { + if args.list { + println!("List mode does not create an output file"); + std::process::exit(1); + } + } + } + + let path = args.file.as_deref().unwrap(); + let pd = path.display(); + + let mut file = match File::open(&path) { + Err(e) => { + eprintln!("Could not open {}: {}", pd, e); + std::process::exit(1) + }, + Ok(file) => file, + }; + + let mut fv: efi::Volume = match file.read_le() { + Err(e) => { + eprintln!("Could not parse {}: {}", pd, e); + std::process::exit(1) + }, + Ok(v) => v, + }; + + if args.debug { + println!("{:#x?}", fv); + } + + if fv.vars.len() == 0 { + println!("{} is an empty variables file", pd); + } else if args.list { + println!("BOOT OPTIONS"); + println!("------------"); + let mut current: u16 = u16::MAX; + let mut next: u16 = u16::MAX; + + if let Some(bootorder) = fv.boot_order() { + current = bootorder.first; + println!("Bootorder: {:?}", bootorder.order); + } + + if let Some(n) = fv.boot_next() { + next = n; + } + + for be in fv.boot_entries() { + if let Some(ref filter) = args.filter { + if !be.name.contains(filter) { + continue; + } + } + let mut tag = String::new(); + tag.push(if be.slot == current { 'C' } else { ' ' }); + tag.push(if be.slot == next { 'N' } else { ' ' }); + tag.push(if be.attributes & efi::LOAD_OPTION_HIDDEN != 0 + { 'H' } else { ' ' }); + + let btype = match be.btype { + efi::BootEntryType::Unknown => "".to_string(), + ref x => format!(" - [{:?}]", x).to_string(), + }; + + println!("{} [{:<2}] {}{}{}", tag, be.slot, be.title, btype, + if be.uri { " [HTTP]" } else { "" }); + + if args.verbose { + //println!("{:#x?}", be); + if be.pathlist.len() > 0 { + for (i, p) in be.pathlist.into_iter().enumerate() { + println!( + " File path {:2x} Type: {:#x}/{:#x} \ + Length: {:#x}", + i, p.device_type, p.sub_type, p.length); + print!("{}\n", HEXDUMPER.hexdump(&p.data)); + } + } + if be.optionaldata.len() > 0 { + println!(" Optional Data:"); + print!("{}\n", HEXDUMPER.hexdump(&be.optionaldata)); + } + println!(""); + } + } + println!("C - Current (first in boot order)"); + println!(" N - Next Boot"); + println!(" H - Hidden"); + } else if let Some(opath) = args.output { + if args.defrag { + fv.defrag(); + } + + if let Some(bootid) = args.bootnext { + fv.remove_var("BootNext", + &efi::EFI_GLOBAL_VARIABLE_GUID.to_string()); + let bootnext = efi::AuthVariable { + name: "BootNext".to_string(), + namelen: 18, + datalen: 2, + data: vec![bootid, 0], + ..Default::default() + }; + if args.debug { + println!("Adding variable: {:#x?}", bootnext); + } + fv.vars.push(bootnext); + } + + let mut ofile = match OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&opath) { + Err(e) => { + eprintln!("Could not open output file {}: {}", + opath.display(), e); + std::process::exit(1) + }, + Ok(file) => file, + }; + + fv.write_to(&mut ofile).expect("Could not write to output file"); + } else { + // Display variables + for v in &fv.vars { + if args.all || v.state == efi::VAR_ADDED { + if let Some(ref filter) = args.filter { + if !v.name.contains(filter) { + continue; + } + } + display_var(&v, args.verbose); + } + } + } +} + +lazy_static! { + static ref HEXDUMPER: rhexdump::Rhexdump = { + let mut rhx = rhexdump::Rhexdump::default(); + rhx.display_duplicate_lines(false); + rhx + }; +} + +fn display_var(v: &efi::AuthVariable, verbose: bool) { + println!("{}", v); + if verbose { + print!("GUID: {} ", v.guid); + println!("Date: {}", v.timestamp); + print!("State: {:2x} ", v.state); + println!("Attrs: {:x}", v.attributes); + println!("{}\n", HEXDUMPER.hexdump(&v.data)); + } +} +