diff --git a/.github/workflows/developer_productivity.yml b/.github/workflows/developer_productivity.yml index f0435a617..944727247 100644 --- a/.github/workflows/developer_productivity.yml +++ b/.github/workflows/developer_productivity.yml @@ -33,7 +33,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@v27 with: # This channel is only required to invoke "nix-shell". # Everything inside that nix-shell will use a pinned version of nixpkgs. diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 0ddea1962..080857889 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -7,4 +7,4 @@ jobs: steps: - uses: actions/checkout@v4 # Executes "typos ." - - uses: crate-ci/typos@v1.20.8 + - uses: crate-ci/typos@v1.22.9 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4858c5d8..44b538998 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,6 @@ jobs: - uses: actions/checkout@v4 - run: | cargo install auto-release - auto-release --condition body -p uefi-raw -p uefi-macros -p uefi -p uefi-services + auto-release --condition body -p uefi-raw -p uefi-macros -p uefi env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5436a4086..24c8ff538 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -114,12 +114,20 @@ jobs: run: cp .github/workflows/msrv_toolchain.toml rust-toolchain.toml - uses: Swatinem/rust-cache@v2 - name: Build - run: cargo xtask build + # Build uefi-test-runner since its dependency tree includes all the + # library packages. Note that xtask isn't used or built here; since it's + # just a dev tool we don't care about the MSRV for that package. + run: cargo build --target x86_64-unknown-uefi -p uefi-test-runner # This job requires the nightly channel, but keep it as a separate job from # `nightly_channel` because it takes a while to run. build_feature_permutations: name: Build (feature permutations) runs-on: ubuntu-latest + env: + # TODO: temporarily allow warnings to not be errors on nightly due to + # incorrect dead_code lint. + # https://github.com/rust-osdev/uefi-rs/issues/1205 + RUSTFLAGS: "" steps: - name: Checkout sources uses: actions/checkout@v4 @@ -131,6 +139,11 @@ jobs: nightly_channel: name: Build (nightly + unstable feature) runs-on: ubuntu-latest + env: + # TODO: temporarily allow warnings to not be errors on nightly due to + # incorrect dead_code lint. + # https://github.com/rust-osdev/uefi-rs/issues/1205 + RUSTFLAGS: "" steps: - name: Checkout sources uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 1d263f4af..39bd96a03 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ # Files generated by mdBook. /book/book/ + +# Integration test output by QEMU. +integration-test-debugcon.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f0921a7..d996c1222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,4 +3,3 @@ * [`uefi` changelog](uefi/CHANGELOG.md) * [`uefi-macros` changelog](uefi-macros/CHANGELOG.md) * [`uefi-raw` changelog](uefi-raw/CHANGELOG.md) -* [`uefi-services` changelog](uefi-services/CHANGELOG.md) diff --git a/Cargo.lock b/Cargo.lock index 398511bf9..fecb0d9ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,36 +10,36 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bincode" @@ -64,9 +64,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -97,12 +97,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" [[package]] name = "cfg-if" @@ -112,15 +109,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.4.18" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -128,9 +125,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstyle", "clap_lex", @@ -138,21 +135,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.68", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "cpufeatures" @@ -165,9 +162,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -180,9 +177,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -209,9 +206,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -221,19 +218,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fatfs" @@ -255,14 +252,14 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -304,9 +301,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -321,9 +318,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -331,11 +328,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -343,9 +346,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -353,36 +356,36 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lzma-rs" @@ -409,26 +412,26 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "nix" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -447,7 +450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -458,9 +461,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -517,9 +520,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -529,9 +532,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -540,42 +543,43 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", @@ -587,15 +591,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.2.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -604,9 +608,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -619,9 +623,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -637,20 +641,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.68", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -659,9 +663,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -685,9 +689,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -702,9 +706,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -719,9 +723,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -737,7 +741,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -751,29 +755,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.68", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -786,9 +790,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.11" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -798,18 +802,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.7" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -820,12 +824,11 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.91" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad7eb6319ebadebca3dacf1f85a93bc54b73dd81b9036795f73de7ddfe27d5a" +checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ "glob", - "once_cell", "serde", "serde_derive", "serde_json", @@ -850,9 +853,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.28.0" +version = "0.29.0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "log", "ptr_meta", @@ -865,31 +868,24 @@ dependencies = [ [[package]] name = "uefi-macros" -version = "0.13.0" +version = "0.14.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.68", "trybuild", "uefi", ] [[package]] name = "uefi-raw" -version = "0.5.2" +version = "0.6.0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "ptr_meta", "uguid", ] -[[package]] -name = "uefi-services" -version = "0.25.0" -dependencies = [ - "uefi", -] - [[package]] name = "uefi-test-runner" version = "0.2.0" @@ -926,9 +922,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -941,9 +937,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.6" +version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ "base64", "flate2", @@ -958,9 +954,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -991,51 +987,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] -[[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.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" 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" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-sys", ] [[package]] @@ -1044,128 +1009,78 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -1198,7 +1113,7 @@ dependencies = [ "clap", "fatfs", "fs-err", - "heck", + "heck 0.4.1", "itertools", "lzma-rs", "mbrman", @@ -1209,7 +1124,7 @@ dependencies = [ "regex", "serde_json", "sha2", - "syn 2.0.58", + "syn 2.0.68", "tar", "tempfile", "ureq", @@ -1218,6 +1133,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 7a8639d9b..89e9533f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "uefi", "uefi-macros", "uefi-raw", - "uefi-services", "uefi-test-runner", "xtask", ] @@ -29,7 +28,6 @@ uguid = "2.1.0" uefi = { path = "uefi" } uefi-macros = { path = "uefi-macros" } uefi-raw = { path = "uefi-raw" } -uefi-services = { path = "uefi-services" } # Enable optimization for xtask itself, not for its dependencies. This speeds up # OVMF prebuilt decompression without much increase in compilation time. diff --git a/PUBLISHING.md b/PUBLISHING.md index e855031a2..1e3e9759d 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -11,7 +11,6 @@ repository to [crates.io](https://crates.io/). release is performed. - We want git tags like `uefi-raw-v0.4.0` or `uefi-v0.25.0` for every release. - We want our crate dependencies published in the right order (if necessary): - - `uefi-services` depends on `uefi` - `uefi` depends on `uefi-macros` and `uefi-raw` ## How: Ways to Publish diff --git a/book/src/reference.md b/book/src/reference.md index 5c525a2d6..00db5891b 100644 --- a/book/src/reference.md +++ b/book/src/reference.md @@ -4,5 +4,4 @@ * [`uefi` crate reference](https://docs.rs/uefi) * [`uefi-macros` crate reference](https://docs.rs/uefi-macros) * [`uefi-raw` crate reference](https://docs.rs/uefi-raw) -* [`uefi-services` crate reference](https://docs.rs/uefi-services) * [UEFI Specifications](https://uefi.org/specifications) diff --git a/book/src/tutorial/app.md b/book/src/tutorial/app.md index 272ccd078..d4990eb26 100644 --- a/book/src/tutorial/app.md +++ b/book/src/tutorial/app.md @@ -16,7 +16,15 @@ cd my-uefi-app Add a few dependencies: ```sh -cargo add log uefi +cargo add log +cargo add uefi --features logger,panic_handler +``` + +to your `Cargo.toml`. The resulting `Cargo.toml` should look like that: +```toml +[dependencies] +log = "0.4.21" +uefi = { version = "0.29.0", features = [ "panic_handler", "logger" ] } ``` Replace the contents of `src/main.rs` with this: diff --git a/nix/sources.json b/nix/sources.json index 2d2d7cb11..33ce93db6 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -1,14 +1,14 @@ { "nixpkgs": { - "branch": "nixos-23.11", + "branch": "nixos-24.05", "description": "Nix Packages collection", "homepage": null, "owner": "NixOS", "repo": "nixpkgs", - "rev": "e38d7cb66ea4f7a0eb6681920615dfcc30fc2920", - "sha256": "1shml3mf52smfra0x3mpfixddr4krp3n78fc2sv07ghiphn22k43", + "rev": "dd457de7e08c6d06789b1f5b88fc9327f4d96309", + "sha256": "1kpamwmvs5xrmjgl3baxphmm69i0qydvgvk1n1c582ii4bdnzky0", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/e38d7cb66ea4f7a0eb6681920615dfcc30fc2920.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/dd457de7e08c6d06789b1f5b88fc9327f4d96309.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "rust-overlay": { @@ -17,10 +17,10 @@ "homepage": "", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ece8bdb3c3b58def25f204b9a1261dee55d7c9c0", - "sha256": "1avinp5vcaicbjssmfwwfii5v8gab6ygh2hfmg4jli822469p5si", + "rev": "c9a793a5278f711a59fe77b9bf54b215667022c6", + "sha256": "0q6k94320ivsjb5jjldywy68q6jb0qbd6lsji86ig8a54l649jcf", "type": "tarball", - "url": "https://github.com/oxalica/rust-overlay/archive/ece8bdb3c3b58def25f204b9a1261dee55d7c9c0.tar.gz", + "url": "https://github.com/oxalica/rust-overlay/archive/c9a793a5278f711a59fe77b9bf54b215667022c6.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/template/Cargo.toml b/template/Cargo.toml index 109736564..08cc62dee 100644 --- a/template/Cargo.toml +++ b/template/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" publish = false [dependencies] -uefi = { version = "0.28.0", features = ["alloc"] } +uefi = { version = "0.29.0", features = ["panic_handler"] } diff --git a/uefi-macros/CHANGELOG.md b/uefi-macros/CHANGELOG.md index a86e687e4..ab6c8829b 100644 --- a/uefi-macros/CHANGELOG.md +++ b/uefi-macros/CHANGELOG.md @@ -1,5 +1,15 @@ # uefi-macros - [Unreleased] + +# uefi-macros - 0.14.0 (2024-07-02) + +## Changed +- The `entry` macro now sets the global system table pointer with `uefi::set_system_table`. + +## Removed +- Removed the `cstr8` and `cstr16` macros. Use the declarative macros of the + same names exported by the `uefi` crate as a replacement. + # uefi-macros - 0.13.0 (2023-11-12) ## Changed diff --git a/uefi-macros/Cargo.toml b/uefi-macros/Cargo.toml index c7f21b899..df2c3fc35 100644 --- a/uefi-macros/Cargo.toml +++ b/uefi-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uefi-macros" -version = "0.13.0" +version = "0.14.0" readme = "README.md" description = "Procedural macros for the `uefi` crate." diff --git a/uefi-macros/src/lib.rs b/uefi-macros/src/lib.rs index 971bf2c9f..09a67716e 100644 --- a/uefi-macros/src/lib.rs +++ b/uefi-macros/src/lib.rs @@ -9,7 +9,7 @@ use quote::{quote, quote_spanned, TokenStreamExt}; use syn::spanned::Spanned; use syn::{ parse_macro_input, parse_quote, Error, Expr, ExprLit, ExprPath, FnArg, Ident, ItemFn, - ItemStruct, Lit, LitStr, Pat, Visibility, + ItemStruct, Lit, Pat, Visibility, }; macro_rules! err { @@ -204,6 +204,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { parse_quote! { unsafe { #system_table_ident.boot_services().set_image_handle(#image_handle_ident); + ::uefi::table::set_system_table(#system_table_ident.as_ptr().cast()); } }, ); @@ -247,88 +248,3 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }; result.into() } - -/// Builds a `CStr8` literal at compile time from a string literal. -/// -/// This will throw a compile error if an invalid character is in the passed string. -/// -/// # Example -/// ``` -/// # use uefi_macros::cstr8; -/// // Empty string -/// assert_eq!(cstr8!().to_u16_slice_with_nul(), [0]); -/// assert_eq!(cstr8!("").to_u16_slice_with_nul(), [0]); -/// // Non-empty string -/// assert_eq!(cstr8!("test").as_bytes(), [116, 101, 115, 116, 0]); -/// ``` -#[proc_macro] -pub fn cstr8(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Accept empty input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - let input: LitStr = parse_macro_input!(input); - let input = input.value(); - // Accept "" input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - - // Accept any non-empty string input. - match input - .chars() - .map(u8::try_from) - .collect::, _>>() - { - Ok(c) => { - quote!(unsafe { ::uefi::CStr8::from_bytes_with_nul_unchecked(&[ #(#c),* , 0 ]) }).into() - } - Err(_) => syn::Error::new_spanned(input, "invalid character in string") - .into_compile_error() - .into(), - } -} - -/// Builds a `CStr16` literal at compile time from a string literal. -/// -/// This will throw a compile error if an invalid character is in the passed string. -/// -/// # Example -/// ```rust -/// # use uefi_macros::cstr16; -/// // Empty string -/// assert_eq!(cstr16!().to_u16_slice_with_nul(), [0]); -/// assert_eq!(cstr16!("").to_u16_slice_with_nul(), [0]); -/// // Non-empty string -/// assert_eq!(cstr16!("test €").to_u16_slice_with_nul(), [116, 101, 115, 116, 32, 8364, 0]); -/// ``` -#[proc_macro] -pub fn cstr16(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Accept empty input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - let input: LitStr = parse_macro_input!(input); - let input = input.value(); - // Accept "" input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - - // Accept any non-empty string input. - match input - .chars() - .map(|c| u16::try_from(c as u32)) - .collect::, _>>() - { - Ok(c) => { - quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[ #(#c),* , 0 ]) }).into() - } - Err(_) => syn::Error::new_spanned( - input, - "There are UTF-8 characters that can't be transformed to UCS-2 character", - ) - .into_compile_error() - .into(), - } -} diff --git a/uefi-macros/tests/ui/fail/entry_bad_return_type.stderr b/uefi-macros/tests/ui/fail/entry_bad_return_type.stderr index 52e4a7cfb..4c7d1674e 100644 --- a/uefi-macros/tests/ui/fail/entry_bad_return_type.stderr +++ b/uefi-macros/tests/ui/fail/entry_bad_return_type.stderr @@ -4,5 +4,5 @@ error[E0308]: mismatched types 8 | fn main(_handle: Handle, _st: SystemTable) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Status`, found `bool` | - = note: expected fn pointer `extern "efiapi" fn(uefi::Handle, uefi::table::SystemTable<_>) -> Status` - found fn pointer `extern "efiapi" fn(uefi::Handle, uefi::table::SystemTable<_>) -> bool` + = note: expected fn pointer `extern "efiapi" fn(uefi::Handle, uefi::table::SystemTable) -> Status` + found fn pointer `extern "efiapi" fn(uefi::Handle, uefi::table::SystemTable) -> bool` diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index 8195515b1..337e917bf 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -1,6 +1,15 @@ # uefi-raw - [Unreleased] +# uefi-raw - 0.6.0 (2024-07-02) + +## Added +- Added `ResetNotificationProtocol`. + +## Changed +- `maximum_capsule_size` of `query_capsule_capabilities` now takes a *mut u64 instead of a *mut usize. +- `ResetType` now derives the `Default` trait. + # uefi-raw - 0.5.2 (2024-04-19) ## Added diff --git a/uefi-raw/Cargo.toml b/uefi-raw/Cargo.toml index 682b9c342..5614d23ac 100644 --- a/uefi-raw/Cargo.toml +++ b/uefi-raw/Cargo.toml @@ -1,8 +1,12 @@ [package] name = "uefi-raw" -version = "0.5.2" +version = "0.6.0" readme = "README.md" -description = "Raw UEFI types" +description = """ +Raw UEFI types and bindings for protocols, boot, and runtime services. This can +serve as base for an UEFI firmware implementation or a high-level wrapper to +access UEFI functionality from an UEFI image. +""" authors.workspace = true categories.workspace = true @@ -10,7 +14,9 @@ edition.workspace = true keywords.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true +# uefi-raw is much less likely to need the latest bleeding-edge features. +# Hence, it is okay to not use the workspace MSRV. +rust-version = "1.70" [dependencies] bitflags.workspace = true diff --git a/uefi-raw/src/protocol/misc.rs b/uefi-raw/src/protocol/misc.rs index a7a3a905a..0147fce87 100644 --- a/uefi-raw/src/protocol/misc.rs +++ b/uefi-raw/src/protocol/misc.rs @@ -1,3 +1,4 @@ +use crate::table::runtime; use crate::{guid, Guid, Status}; #[derive(Debug)] @@ -22,3 +23,25 @@ pub struct TimestampProperties { /// example, a 24-bit counter would have an end value of `0xff_ffff`. pub end_value: u64, } + +/// Properties of Reset Notification. +#[derive(Debug)] +#[repr(C)] +pub struct ResetNotificationProtocol { + pub register_reset_notify: + unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, + pub unregister_reset_notify: + unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, +} + +impl ResetNotificationProtocol { + pub const GUID: Guid = guid!("9da34ae0-eaf9-4bbf-8ec3-fd60226c44be"); +} + +/// Raw reset notification function, to be called if you register it when a ResetSystem() is executed. +pub type ResetSystemFn = unsafe extern "efiapi" fn( + rt: runtime::ResetType, + status: Status, + data_size: usize, + data: *const u8, +); diff --git a/uefi-raw/src/table/boot.rs b/uefi-raw/src/table/boot.rs index 5e325d189..f0d3bbf02 100644 --- a/uefi-raw/src/table/boot.rs +++ b/uefi-raw/src/table/boot.rs @@ -326,12 +326,30 @@ bitflags! { } } -/// A structure describing a region of memory. +/// A structure describing a region of memory. This type corresponds to [version] +/// of this struct in the UEFI spec and is always bound to a corresponding +/// UEFI memory map. +/// +/// # UEFI pitfalls +/// As of May 2024: +/// The memory descriptor struct might be extended in the future by a new UEFI +/// spec revision, which will be reflected in another `version` of that +/// descriptor. The version is reported when using `get_memory_map` of +/// [`BootServices`]. +/// +/// Also note that you **must never** work with `size_of::` +/// but always with `desc_size`, which is reported when using `get_memory_map` +/// as well [[0]]. For example, although the actual size is of version 1 +/// descriptors is `40`, the reported `desc_size` is `48`. +/// +/// [version]: MemoryDescriptor::VERSION +/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct MemoryDescriptor { /// Type of memory occupying this range. pub ty: MemoryType, + // Implicit 32-bit padding. /// Starting physical address. pub phys_start: PhysicalAddress, /// Starting virtual address. diff --git a/uefi-raw/src/table/runtime.rs b/uefi-raw/src/table/runtime.rs index e6474e71d..69daa0f23 100644 --- a/uefi-raw/src/table/runtime.rs +++ b/uefi-raw/src/table/runtime.rs @@ -66,7 +66,7 @@ pub struct RuntimeServices { pub query_capsule_capabilities: unsafe extern "efiapi" fn( capsule_header_array: *const *const CapsuleHeader, capsule_count: usize, - maximum_capsule_size: *mut usize, + maximum_capsule_size: *mut u64, reset_type: *mut ResetType, ) -> Status, @@ -80,6 +80,7 @@ pub struct RuntimeServices { } newtype_enum! { + #[derive(Default)] /// The type of system reset. pub enum ResetType: u32 => { /// System-wide reset. diff --git a/uefi-services/CHANGELOG.md b/uefi-services/CHANGELOG.md deleted file mode 100644 index eeebcac7a..000000000 --- a/uefi-services/CHANGELOG.md +++ /dev/null @@ -1,105 +0,0 @@ -# uefi-services - [Unreleased] - - -# uefi-services - 0.25.0 (2023-11-12) - -## Changed -- `uefi-services` is deprecated and should be removed. All functionality was -moved to `uefi::helpers::init()` - - -# uefi-services - 0.24.0 (2023-03-17) - -## Changed -- Updated to latest version of `uefi`. - - -# uefi-services - 0.23.0 (2023-11-12) - -## Changed -- `uefi_services::system_table` now returns `SystemTable` directly, rather - than wrapped in a `NonNull` pointer. - - -# uefi-services - 0.22.0 (2023-10-11) - -## Changed -- Updated to latest version of `uefi`. - - -# uefi-services - 0.21.0 (2023-06-20) - -## Changed -- Updated to latest version of `uefi`. - - -# uefi-services - 0.20.0 (2023-06-04) - -## Changed -- Updated to latest version of `uefi`. - - -# uefi-services - 0.19.0 (2023-06-01) - -## Changed -- Internal updates for changes in `uefi`. - - -# uefi-services - 0.18.0 (2023-05-15) - -## Changed -- Internal updates for changes in `uefi`. - - -# uefi-services - 0.17.0 (2023-03-19) - -## Changed -- Drop use of unstable `alloc_error_handler` feature. As of Rust 1.68 we can use - [`default_alloc_error_handler`](https://github.com/rust-lang/rust/pull/102318) - instead. - - -# uefi-services - 0.16.0 (2023-01-16) - -## Changed -- Bumped `uefi` dependency to latest version. - - -# uefi-services - 0.15.0 (2022-11-15) - -## Changed -- Changed the panic handler log message to use `println!` instead of - `error!`. This removes an extraneous file name and line number from - the log message. -- Added a `logger` feature which reflects the same feature in `uefi`. - This allows using both crates while disabling `logger` in `uefi`, - which was previously impossible. - - -# uefi-services - 0.14.0 (2022-09-09) - -## Added -- Added `print!` and `println!` macros. - -## Changed -- The `no_panic_handler` feature has been replaced with an additive - `panic_handler` feature. The new feature is enabled by default. - - -# uefi-services - 0.13.1 (2022-08-26) - -## Changed -- Relaxed the version requirements for the `log` dependency to allow - earlier patch versions. - - -# uefi-services - 0.13.0 (2022-05-16) - -## Changed -- Bumped `uefi` dependency to latest version. - - -# uefi-services - 0.12.1 (2022-03-15) - -## Changed -- Updated to the 2021 edition. diff --git a/uefi-services/Cargo.toml b/uefi-services/Cargo.toml deleted file mode 100644 index 41797dd46..000000000 --- a/uefi-services/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "uefi-services" -version = "0.25.0" -readme = "README.md" -description = "Deprecated. Please migrate to `uefi::helpers`." - -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -uefi = { version = "0.28.0", features = ["global_allocator"] } - -[features] -default = ["panic_handler", "logger"] -# Enable QEMU-specific functionality -qemu = ["uefi/qemu"] -panic_handler = [ "uefi/panic_handler" ] -logger = ["uefi/logger"] diff --git a/uefi-services/LICENSE b/uefi-services/LICENSE deleted file mode 100644 index a612ad981..000000000 --- a/uefi-services/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - 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 and Conditions --------------------------------- - -2.1. Grants - -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 such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -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 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 every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, 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 any 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 under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, 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. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the 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. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/uefi-services/README.md b/uefi-services/README.md index 642763126..f240f5870 100644 --- a/uefi-services/README.md +++ b/uefi-services/README.md @@ -1,3 +1,4 @@ # uefi-services -WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`. +WARNING: `uefi-services` is deprecated. Functionality was moved to +`uefi::helpers::init` in `uefi@v0.28.0`. diff --git a/uefi-services/build.rs b/uefi-services/build.rs deleted file mode 100644 index 96cea2f34..000000000 --- a/uefi-services/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("cargo:warning=`uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."); -} diff --git a/uefi-services/src/lib.rs b/uefi-services/src/lib.rs deleted file mode 100644 index 3b73e4c16..000000000 --- a/uefi-services/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`. -#![no_std] - -use uefi::prelude::*; -use uefi::Event; -use uefi::Result; - -pub use uefi::{print, println}; - -/// Deprecated. Use [`uefi::helpers::init`] instead. -#[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."] -pub fn init(st: &mut SystemTable) -> Result> { - uefi::helpers::init(st).map(|_| None) -} - -/// Deprecated. Use [`uefi::helpers::system_table`] instead. -#[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::system_table`."] -pub fn system_table() -> SystemTable { - uefi::helpers::system_table() -} diff --git a/uefi-test-runner/examples/sierpinski.rs b/uefi-test-runner/examples/sierpinski.rs index bbd2de48c..ac690eaef 100644 --- a/uefi-test-runner/examples/sierpinski.rs +++ b/uefi-test-runner/examples/sierpinski.rs @@ -55,6 +55,23 @@ impl Buffer { dims: (self.width, self.height), }) } + + /// Update only a pixel to the framebuffer. + fn blit_pixel( + &self, + gop: &mut GraphicsOutput, + coords: (usize, usize), + ) -> Result { + gop.blt(BltOp::BufferToVideo { + buffer: &self.pixels, + src: BltRegion::SubRectangle { + coords, + px_stride: self.width, + }, + dest: coords, + dims: (1, 1), + }) + } } // ANCHOR_END: buffer @@ -90,6 +107,9 @@ fn draw_sierpinski(bt: &BootServices) -> Result { } } + // Draw background. + buffer.blit(&mut gop)?; + let size = Point::new(width as f32, height as f32); // Define the vertices of a big triangle. @@ -119,7 +139,7 @@ fn draw_sierpinski(bt: &BootServices) -> Result { pixel.blue = 0; // Draw the buffer to the screen. - buffer.blit(&mut gop)?; + buffer.blit_pixel(&mut gop, (p.x as usize, p.y as usize))?; } } diff --git a/uefi-test-runner/examples/timestamp.rs b/uefi-test-runner/examples/timestamp.rs new file mode 100644 index 000000000..deccc7e98 --- /dev/null +++ b/uefi-test-runner/examples/timestamp.rs @@ -0,0 +1,64 @@ +// ANCHOR: all +// ANCHOR: features +#![no_main] +#![no_std] +// ANCHOR_END: features + +extern crate alloc; + +use log::{info, warn}; + +// ANCHOR: use +use uefi::prelude::*; +use uefi::proto::misc::Timestamp; + +// ANCHOR_END: use + +// ANCHOR: entry +#[entry] +fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { + // ANCHOR_END: entry + // ANCHOR: services + uefi::helpers::init(&mut system_table).unwrap(); + let boot_services = system_table.boot_services(); + // ANCHOR_END: services + + // ANCHOR: params + test_timestamp(boot_services); + // ANCHOR_END: params + + // ANCHOR: stall + boot_services.stall(10_000_000); + // ANCHOR_END: stall + + // ANCHOR: return + Status::SUCCESS +} +// ANCHOR_END: return + +// ANCHOR: test_timestamp +pub fn test_timestamp(bt: &BootServices) { + // ANCHOR_END: test_timestamp + info!("Running loaded Timestamp Protocol test"); + + let handle = bt.get_handle_for_protocol::(); + + match handle { + Ok(handle) => { + let timestamp_proto = bt + .open_protocol_exclusive::(handle) + .expect("Founded Timestamp Protocol but open failed"); + // ANCHOR: text + let timestamp = timestamp_proto.get_timestamp(); + info!("Timestamp Protocol's timestamp: {:?}", timestamp); + + let properties = timestamp_proto.get_properties(); + info!("Timestamp Protocol's properties: {:?}", properties); + // ANCHOR_END: text + } + Err(err) => { + warn!("Failed to found Timestamp Protocol: {:?}", err); + } + } +} +// ANCHOR_END: all diff --git a/uefi-test-runner/src/boot/memory.rs b/uefi-test-runner/src/boot/memory.rs index 078548991..133110b43 100644 --- a/uefi-test-runner/src/boot/memory.rs +++ b/uefi-test-runner/src/boot/memory.rs @@ -63,45 +63,41 @@ fn alloc_alignment() { fn memory_map(bt: &BootServices) { info!("Testing memory map functions"); - // Get the memory descriptor size and an estimate of the memory map size - let sizes = bt.memory_map_size(); + // Ensure that the memory map is freed after each iteration (on drop). + // Otherwise, we will have an OOM. + for _ in 0..200000 { + let mut memory_map = bt + .memory_map(MemoryType::LOADER_DATA) + .expect("Failed to retrieve UEFI memory map"); - // 2 extra descriptors should be enough. - let buf_sz = sizes.map_size + 2 * sizes.entry_size; + memory_map.sort(); - // We will use vectors for convenience. - let mut buffer = vec![0_u8; buf_sz]; + // Collect the descriptors into a vector + let descriptors = memory_map.entries().copied().collect::>(); - let mut memory_map = bt - .memory_map(&mut buffer) - .expect("Failed to retrieve UEFI memory map"); + // Ensured we have at least one entry. + // Real memory maps usually have dozens of entries. + assert!(!descriptors.is_empty(), "Memory map is empty"); - memory_map.sort(); + let mut curr_value = descriptors[0]; - // Collect the descriptors into a vector - let descriptors = memory_map.entries().copied().collect::>(); - - // Ensured we have at least one entry. - // Real memory maps usually have dozens of entries. - assert!(!descriptors.is_empty(), "Memory map is empty"); - - let mut curr_value = descriptors[0]; - - for value in descriptors.iter().skip(1) { - if value.phys_start <= curr_value.phys_start { - panic!("memory map sorting failed"); + for value in descriptors.iter().skip(1) { + if value.phys_start <= curr_value.phys_start { + panic!("memory map sorting failed"); + } + curr_value = *value; } - curr_value = *value; - } - // This is pretty much a sanity test to ensure returned memory isn't filled with random values. - let first_desc = descriptors[0]; + // This is pretty much a basic sanity test to ensure returned memory + // isn't filled with random values. + let first_desc = descriptors[0]; - #[cfg(target_arch = "x86_64")] - { - let phys_start = first_desc.phys_start; - assert_eq!(phys_start, 0, "Memory does not start at address 0"); + #[cfg(target_arch = "x86_64")] + { + let phys_start = first_desc.phys_start; + assert_eq!(phys_start, 0, "Memory does not start at address 0"); + } + let page_count = first_desc.page_count; + assert!(page_count != 0, "Memory map entry has size zero"); } - let page_count = first_desc.page_count; - assert!(page_count != 0, "Memory map entry has zero size"); } diff --git a/uefi-test-runner/src/boot/misc.rs b/uefi-test-runner/src/boot/misc.rs index 0399ff2ba..8b7422be4 100644 --- a/uefi-test-runner/src/boot/misc.rs +++ b/uefi-test-runner/src/boot/misc.rs @@ -121,7 +121,8 @@ fn test_install_protocol_interface(bt: &BootServices) { mem::size_of::(), ) .unwrap() - .cast(); + .cast() + .as_ptr(); unsafe { alloc.write(TestProtocol { data: 123 }) }; let _ = unsafe { @@ -187,7 +188,8 @@ fn test_install_configuration_table(st: &SystemTable) { let config = st .boot_services() .allocate_pool(MemoryType::ACPI_RECLAIM, 1) - .expect("Failed to allocate config table"); + .expect("Failed to allocate config table") + .as_ptr(); unsafe { config.write(42) }; let count = st.config_table().len(); diff --git a/uefi-test-runner/src/main.rs b/uefi-test-runner/src/main.rs index b3a3e7ad4..c61411e27 100644 --- a/uefi-test-runner/src/main.rs +++ b/uefi-test-runner/src/main.rs @@ -13,8 +13,7 @@ use uefi::proto::console::serial::Serial; use uefi::proto::device_path::build::{self, DevicePathBuilder}; use uefi::proto::device_path::messaging::Vendor; use uefi::table::boot::MemoryType; -use uefi::Result; -use uefi::{print, println}; +use uefi::{print, println, Result}; mod boot; mod fs; @@ -56,7 +55,7 @@ fn efi_main(image: Handle, mut st: SystemTable) -> Status { boot::test(&st); // Test all the supported protocols. - proto::test(image, &mut st); + proto::test(&mut st); // TODO: runtime services work before boot services are exited, but we'd // probably want to test them after exit_boot_services. However, @@ -196,10 +195,23 @@ fn shutdown(mut st: SystemTable) -> ! { // type of regression this prevents. info!("LOGGING_STILL_WORKING_RIGHT_BEFORE_EBS"); - info!("Testing complete, shutting down..."); + info!("Testing complete, exiting boot services..."); // Exit boot services as a proof that it works :) - let (st, _iter) = st.exit_boot_services(MemoryType::LOADER_DATA); + let (st, mmap) = unsafe { st.exit_boot_services(MemoryType::LOADER_DATA) }; + + info!("Memory Map:"); + for desc in mmap.entries() { + info!( + "start=0x{:016x} size=0x{:016x} type={:?}, attr={:?}", + desc.phys_start, + desc.page_count * 4096, + desc.ty, + desc.att + ); + } + + info!("Shutting down..."); #[cfg(target_arch = "x86_64")] { diff --git a/uefi-test-runner/src/proto/console/gop.rs b/uefi-test-runner/src/proto/console/gop.rs index 8ce714351..94bcbda40 100644 --- a/uefi-test-runner/src/proto/console/gop.rs +++ b/uefi-test-runner/src/proto/console/gop.rs @@ -3,7 +3,7 @@ use uefi::prelude::*; use uefi::proto::console::gop::{BltOp, BltPixel, FrameBuffer, GraphicsOutput, PixelFormat}; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams}; -pub unsafe fn test(image: Handle, bt: &BootServices) { +pub unsafe fn test(bt: &BootServices) { info!("Running graphics output protocol test"); let handle = bt .get_handle_for_protocol::() @@ -12,7 +12,7 @@ pub unsafe fn test(image: Handle, bt: &BootServices) { .open_protocol::( OpenProtocolParams { handle, - agent: image, + agent: bt.image_handle(), controller: None, }, // For this test, don't open in exclusive mode. That diff --git a/uefi-test-runner/src/proto/console/mod.rs b/uefi-test-runner/src/proto/console/mod.rs index f11b2ef01..8dd7392ec 100644 --- a/uefi-test-runner/src/proto/console/mod.rs +++ b/uefi-test-runner/src/proto/console/mod.rs @@ -1,6 +1,6 @@ use uefi::prelude::*; -pub fn test(image: Handle, st: &mut SystemTable) { +pub fn test(st: &mut SystemTable) { info!("Testing console protocols"); stdout::test(st.stdout()); @@ -8,7 +8,7 @@ pub fn test(image: Handle, st: &mut SystemTable) { let bt = st.boot_services(); unsafe { serial::test(bt); - gop::test(image, bt); + gop::test(bt); } pointer::test(bt); } diff --git a/uefi-test-runner/src/proto/device_path.rs b/uefi-test-runner/src/proto/device_path.rs index fb99d5792..942837241 100644 --- a/uefi-test-runner/src/proto/device_path.rs +++ b/uefi-test-runner/src/proto/device_path.rs @@ -5,13 +5,13 @@ use uefi::proto::device_path::text::*; use uefi::proto::device_path::{DevicePath, LoadedImageDevicePath}; use uefi::proto::loaded_image::LoadedImage; -pub fn test(image: Handle, bt: &BootServices) { +pub fn test(bt: &BootServices) { info!("Running device path protocol test"); // test 1/2: test low-level API by directly opening all protocols { let loaded_image = bt - .open_protocol_exclusive::(image) + .open_protocol_exclusive::(bt.image_handle()) .expect("Failed to open LoadedImage protocol"); let device_path = bt @@ -55,7 +55,7 @@ pub fn test(image: Handle, bt: &BootServices) { // Get the `LoadedImageDevicePath`. Verify it start with the same nodes as // `device_path`. let loaded_image_device_path = bt - .open_protocol_exclusive::(image) + .open_protocol_exclusive::(bt.image_handle()) .expect("Failed to open LoadedImageDevicePath protocol"); for (n1, n2) in device_path @@ -69,7 +69,7 @@ pub fn test(image: Handle, bt: &BootServices) { // test 2/2: test high-level to-string api { let loaded_image_device_path = bt - .open_protocol_exclusive::(image) + .open_protocol_exclusive::(bt.image_handle()) .expect("Failed to open LoadedImageDevicePath protocol"); let device_path: &DevicePath = &loaded_image_device_path; diff --git a/uefi-test-runner/src/proto/loaded_image.rs b/uefi-test-runner/src/proto/loaded_image.rs index 93e499b06..648db3ac5 100644 --- a/uefi-test-runner/src/proto/loaded_image.rs +++ b/uefi-test-runner/src/proto/loaded_image.rs @@ -1,11 +1,11 @@ use uefi::prelude::*; use uefi::proto::loaded_image::LoadedImage; -pub fn test(image: Handle, bt: &BootServices) { +pub fn test(bt: &BootServices) { info!("Running loaded image protocol test"); let loaded_image = bt - .open_protocol_exclusive::(image) + .open_protocol_exclusive::(bt.image_handle()) .expect("Failed to open LoadedImage protocol"); let load_options = loaded_image.load_options_as_bytes(); diff --git a/uefi-test-runner/src/proto/misc.rs b/uefi-test-runner/src/proto/misc.rs new file mode 100644 index 000000000..c7b1664f7 --- /dev/null +++ b/uefi-test-runner/src/proto/misc.rs @@ -0,0 +1,44 @@ +use uefi::prelude::*; +use uefi::proto::misc::ResetNotification; +use uefi::table::runtime; + +pub fn test(bt: &BootServices) { + test_reset_notification(bt); +} + +pub fn test_reset_notification(bt: &BootServices) { + info!("Running loaded ResetNotification protocol test"); + + let handle = bt + .get_handle_for_protocol::() + .expect("Failed to get handles for `ResetNotification` protocol"); + + let mut reset_notif_proto = bt + .open_protocol_exclusive::(handle) + .expect("Founded ResetNotification Protocol but open failed"); + + // value efi_reset_fn is the type of ResetSystemFn, a function pointer + unsafe extern "efiapi" fn efi_reset_fn( + rt: runtime::ResetType, + status: Status, + data_size: usize, + data: *const u8, + ) { + info!("Inside the event callback, hi, efi_reset_fn"); + info!("rt: {:?} status: {:?}", rt, status); + info!("size: {:?} data: {:?}", data_size, data); + // do what you want + } + + let result = reset_notif_proto.register_reset_notify(efi_reset_fn); + info!( + "ResetNotification Protocol register efi_reset_fn test: {:?}", + result + ); + + let result = reset_notif_proto.unregister_reset_notify(efi_reset_fn); + info!( + "ResetNotification Protocol unregister efi_reset_fn test: {:?}", + result + ); +} diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index b232e3cf9..d40a11453 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -1,27 +1,27 @@ use uefi::prelude::*; - use uefi::proto::loaded_image::LoadedImage; use uefi::{proto, Identify}; -pub fn test(image: Handle, st: &mut SystemTable) { +pub fn test(st: &mut SystemTable) { info!("Testing various protocols"); - console::test(image, st); + console::test(st); let bt = st.boot_services(); find_protocol(bt); - test_protocols_per_handle(image, bt); + test_protocols_per_handle(bt); debug::test(bt); - device_path::test(image, bt); + device_path::test(bt); driver::test(bt); - loaded_image::test(image, bt); + loaded_image::test(bt); media::test(bt); network::test(bt); pi::test(bt); rng::test(bt); shell_params::test(bt); string::test(bt); + misc::test(bt); #[cfg(any( target_arch = "x86", @@ -44,9 +44,9 @@ fn find_protocol(bt: &BootServices) { ); } -fn test_protocols_per_handle(image: Handle, bt: &BootServices) { +fn test_protocols_per_handle(bt: &BootServices) { let pph = bt - .protocols_per_handle(image) + .protocols_per_handle(bt.image_handle()) .expect("Failed to get protocols for image handle"); info!("Image handle has {} protocols", pph.len()); @@ -61,6 +61,7 @@ mod device_path; mod driver; mod loaded_image; mod media; +mod misc; mod network; mod pi; mod rng; diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 2770e37ee..c0cf43177 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -1,6 +1,53 @@ # uefi - [Unreleased] +# uefi - 0.29.0 (2024-07-02) + +## Added +- Added `RuntimeServices::update_capsule`. +- Added `RuntimeServices::query_capsule_capabilities`. +- The logger from `uefi::helpers` now also logs to the [debugcon](https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/) + device (QEMU) respectively the debug-console (cloud-hypervisor). This only + works on x86. It is activated by default (only on x86) and can be deactivated + by removing the `log-debugcon` cargo feature. The major benefit is that one + can get log messages even after one exited the boot services. +- Added `table::{set_system_table, system_table_boot, system_table_runtime}`. + This provides an initial API for global tables that do not require passing + around a reference. +- Added `ResetNotification` protocol. +- Added `TryFrom<&[u8]>` for `DevicePathHeader`, `DevicePathNode` and `DevicePath`. +- Added `ByteConversionError`. +- Re-exported `CapsuleFlags`. +- One can now specify in `TimeError` what fields of `Time` are outside its valid + range. `Time::is_valid` has been updated accordingly. +- `MemoryMap::as_raw` which provides raw access to the memory map. This is for + example useful if you create your own Multiboot2 bootloader that embeds the + EFI mmap in a Multiboot2 boot information structure. +- `Mode` is now `Copy` and `Clone`. +- Added `TryFrom<&[u8]>` for `Time`. + +## Changed +- `SystemTable::exit_boot_services` is now `unsafe`. See that method's + documentation for details of obligations for callers. +- `BootServices::allocate_pool` now returns `NonZero` instead of + `*mut u8`. +- `helpers::system_table` is deprecated, use `table::system_table_boot` instead. +- `BootServices::memory_map` changed its signature from \ + `pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result> {` \ + to \ + `pub fn memory_map(&self, mt: MemoryType) -> Result` + - Allocations now happen automatically internally on the UEFI heap. Also, the + returned type is automatically freed on the UEFI heap, as long as boot + services are not excited. By removing the need for that explicit buffer and + the lifetime, the API is simpler. +- `GraphicsOutput::query_mode` is now private. Use `GraphicsOutput::modes` + instead. + +## Removed +- Removed the `panic-on-logger-errors` feature of the `uefi` crate. Logger + errors are now silently ignored. + + # uefi - 0.28.0 (2024-04-19) ## Added diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index d1ba545ba..2fe9e257b 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uefi" -version = "0.28.0" +version = "0.29.0" readme = "README.md" description = "Safe and easy-to-use wrapper for building UEFI apps." @@ -13,7 +13,7 @@ repository.workspace = true rust-version.workspace = true [features] -default = ["panic-on-logger-errors"] +default = [ "log-debugcon" ] alloc = [] # Generic gate to code that uses unstable features of Rust. You usually need a nightly toolchain. @@ -23,11 +23,14 @@ unstable = [] logger = [] global_allocator = [] panic_handler = [] -# Ignore text output errors in logger as a workaround for firmware issues that -# were observed on the VirtualBox UEFI implementation (see uefi-rs#121). -# In those cases, this feature can be excluded by removing the default features. -panic-on-logger-errors = [] -qemu = ["dep:qemu-exit", "panic_handler"] # panic_handler: logical, not technical dependency +# Some convenience when running inside QEMU. +# - dependency log-debugcon: logical, not technical +# - dependency panic_handler: logical, not technical +qemu = ["dep:qemu-exit", "panic_handler", "log-debugcon"] +# Whether the internal logger from the helpers module should also log to +# the debugcon device (QEMU) and debug-console (cloud-hypervisor). Only works +# on x86. +log-debugcon = [] [dependencies] bitflags.workspace = true @@ -35,9 +38,9 @@ log.workspace = true ptr_meta.workspace = true uguid.workspace = true cfg-if = "1.0.0" -ucs2 = "0.3.2" -uefi-macros = "0.13.0" -uefi-raw = "0.5.2" +ucs2 = "0.3.3" +uefi-macros = "0.14.0" +uefi-raw = "0.6.0" qemu-exit = { version = "3.0.2", optional = true } [package.metadata.docs.rs] diff --git a/uefi/src/allocator.rs b/uefi/src/allocator.rs index 3be75c164..d1ef3dc93 100644 --- a/uefi/src/allocator.rs +++ b/uefi/src/allocator.rs @@ -86,7 +86,7 @@ unsafe impl GlobalAlloc for Allocator { // within the allocation. let full_alloc_ptr = if let Ok(ptr) = boot_services.allocate_pool(memory_type, size + align) { - ptr + ptr.as_ptr() } else { return ptr::null_mut(); }; @@ -116,6 +116,7 @@ unsafe impl GlobalAlloc for Allocator { // use `allocate_pool` directly. boot_services .allocate_pool(memory_type, size) + .map(|ptr| ptr.as_ptr()) .unwrap_or(ptr::null_mut()) } } diff --git a/uefi/src/data_types/mod.rs b/uefi/src/data_types/mod.rs index 1238a583d..74239c025 100644 --- a/uefi/src/data_types/mod.rs +++ b/uefi/src/data_types/mod.rs @@ -138,23 +138,27 @@ pub trait Align { } mod guid; -pub use self::guid::{Guid, Identify}; +pub use guid::{Guid, Identify}; pub mod chars; -pub use self::chars::{Char16, Char8}; +pub use chars::{Char16, Char8}; #[macro_use] mod opaque; mod strs; -pub use self::strs::{ +pub use strs::{ CStr16, CStr8, EqStrUntilNul, FromSliceWithNulError, FromStrWithBufError, UnalignedCStr16Error, }; +/// These functions are used in the implementation of the [`cstr8`] macro. +#[doc(hidden)] +pub use strs::{str_num_latin1_chars, str_to_latin1}; + #[cfg(feature = "alloc")] mod owned_strs; #[cfg(feature = "alloc")] -pub use self::owned_strs::{CString16, FromStrError}; +pub use owned_strs::{CString16, FromStrError}; mod unaligned_slice; pub use unaligned_slice::UnalignedSlice; diff --git a/uefi/src/data_types/strs.rs b/uefi/src/data_types/strs.rs index e3819dac1..2855e8802 100644 --- a/uefi/src/data_types/strs.rs +++ b/uefi/src/data_types/strs.rs @@ -221,6 +221,93 @@ impl<'a> TryFrom<&'a CStr> for &'a CStr8 { } } +/// Get a Latin-1 character from a UTF-8 byte slice at the given offset. +/// +/// Returns a pair containing the Latin-1 character and the number of bytes in +/// the UTF-8 encoding of that character. +/// +/// Panics if the string cannot be encoded in Latin-1. +/// +/// # Safety +/// +/// The input `bytes` must be valid UTF-8. +const unsafe fn latin1_from_utf8_at_offset(bytes: &[u8], offset: usize) -> (u8, usize) { + if bytes[offset] & 0b1000_0000 == 0b0000_0000 { + (bytes[offset], 1) + } else if bytes[offset] & 0b1110_0000 == 0b1100_0000 { + let a = (bytes[offset] & 0b0001_1111) as u16; + let b = (bytes[offset + 1] & 0b0011_1111) as u16; + let ch = a << 6 | b; + if ch > 0xff { + panic!("input string cannot be encoded as Latin-1"); + } + (ch as u8, 2) + } else { + // Latin-1 code points only go up to 0xff, so if the input contains any + // UTF-8 characters larger than two bytes it cannot be converted to + // Latin-1. + panic!("input string cannot be encoded as Latin-1"); + } +} + +/// Count the number of Latin-1 characters in a string. +/// +/// Panics if the string cannot be encoded in Latin-1. +/// +/// This is public but hidden; it is used in the `cstr8` macro. +#[must_use] +pub const fn str_num_latin1_chars(s: &str) -> usize { + let bytes = s.as_bytes(); + let len = bytes.len(); + + let mut offset = 0; + let mut num_latin1_chars = 0; + + while offset < len { + // SAFETY: `bytes` is valid UTF-8. + let (_, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, offset) }; + offset += num_utf8_bytes; + num_latin1_chars += 1; + } + + num_latin1_chars +} + +/// Convert a `str` into a null-terminated Latin-1 character array. +/// +/// Panics if the string cannot be encoded in Latin-1. +/// +/// This is public but hidden; it is used in the `cstr8` macro. +#[must_use] +pub const fn str_to_latin1(s: &str) -> [u8; N] { + let bytes = s.as_bytes(); + let len = bytes.len(); + + let mut output = [0; N]; + + let mut output_offset = 0; + let mut input_offset = 0; + while input_offset < len { + // SAFETY: `bytes` is valid UTF-8. + let (ch, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, input_offset) }; + if ch == 0 { + panic!("interior null character"); + } else { + output[output_offset] = ch; + output_offset += 1; + input_offset += num_utf8_bytes; + } + } + + // The output array must be one bigger than the converted string, + // to leave room for the trailing null character. + if output_offset + 1 != N { + panic!("incorrect array length"); + } + + output +} + /// An UCS-2 null-terminated string slice. /// /// This type is largely inspired by [`core::ffi::CStr`] with the exception that all characters are @@ -620,8 +707,8 @@ where #[cfg(test)] mod tests { use super::*; + use crate::{cstr16, cstr8}; use alloc::string::String; - use uefi_macros::{cstr16, cstr8}; // Tests if our CStr8 type can be constructed from a valid core::ffi::CStr #[test] diff --git a/uefi/src/fs/dir_entry_iter.rs b/uefi/src/fs/dir_entry_iter.rs index 09ba03a52..334825e2e 100644 --- a/uefi/src/fs/dir_entry_iter.rs +++ b/uefi/src/fs/dir_entry_iter.rs @@ -1,9 +1,8 @@ //! Module for directory iteration. See [`UefiDirectoryIter`]. use super::*; -use crate::{CStr16, Result}; +use crate::{cstr16, CStr16, Result}; use alloc::boxed::Box; -use uefi_macros::cstr16; /// Common skip dirs in UEFI/FAT-style file systems. pub const COMMON_SKIP_DIRS: &[&CStr16] = &[cstr16!("."), cstr16!("..")]; diff --git a/uefi/src/fs/mod.rs b/uefi/src/fs/mod.rs index d32ee483c..b66acbfcc 100644 --- a/uefi/src/fs/mod.rs +++ b/uefi/src/fs/mod.rs @@ -47,7 +47,7 @@ //! There is no automatic synchronization of the file system for concurrent //! accesses. This is in the responsibility of the user. //! -//! [`cstr16!`]: uefi_macros::cstr16 +//! [`cstr16!`]: crate::cstr16 mod dir_entry_iter; mod file_system; diff --git a/uefi/src/fs/path/mod.rs b/uefi/src/fs/path/mod.rs index ce943d35a..3e1edb9a8 100644 --- a/uefi/src/fs/path/mod.rs +++ b/uefi/src/fs/path/mod.rs @@ -21,7 +21,7 @@ pub use path::{Components, Path}; pub use pathbuf::PathBuf; use crate::data_types::chars::NUL_16; -use crate::{CStr16, Char16}; +use crate::{cstr16, CStr16, Char16}; pub(super) use validation::validate_path; pub use validation::PathError; @@ -29,7 +29,7 @@ pub use validation::PathError; pub const SEPARATOR: Char16 = unsafe { Char16::from_u16_unchecked('\\' as u16) }; /// Stringified version of [`SEPARATOR`]. -pub const SEPARATOR_STR: &CStr16 = uefi_macros::cstr16!("\\"); +pub const SEPARATOR_STR: &CStr16 = cstr16!("\\"); /// Deny list of characters for path components. UEFI supports FAT-like file /// systems. According to , diff --git a/uefi/src/fs/path/path.rs b/uefi/src/fs/path/path.rs index 122ea4965..efa5019d5 100644 --- a/uefi/src/fs/path/path.rs +++ b/uefi/src/fs/path/path.rs @@ -198,8 +198,8 @@ mod convenience_impls { #[cfg(test)] mod tests { use super::*; + use crate::cstr16; use alloc::vec::Vec; - use uefi_macros::cstr16; #[test] fn from_cstr16() { diff --git a/uefi/src/fs/path/pathbuf.rs b/uefi/src/fs/path/pathbuf.rs index 222121f9b..de81441da 100644 --- a/uefi/src/fs/path/pathbuf.rs +++ b/uefi/src/fs/path/pathbuf.rs @@ -120,8 +120,8 @@ mod convenience_impls { #[cfg(test)] mod tests { use super::*; + use crate::cstr16; use alloc::string::ToString; - use uefi_macros::cstr16; #[test] fn from_cstr16() { diff --git a/uefi/src/fs/path/validation.rs b/uefi/src/fs/path/validation.rs index b1b5cb531..144073003 100644 --- a/uefi/src/fs/path/validation.rs +++ b/uefi/src/fs/path/validation.rs @@ -67,8 +67,7 @@ pub fn validate_path>(path: P) -> Result<(), PathError> { mod tests { use super::*; use crate::fs::PathBuf; - use crate::CString16; - use uefi_macros::cstr16; + use crate::{cstr16, CString16}; #[test] fn test_validate_path() { diff --git a/uefi/src/helpers/logger.rs b/uefi/src/helpers/logger.rs index 3fc386bb1..b8d8cfa41 100644 --- a/uefi/src/helpers/logger.rs +++ b/uefi/src/helpers/logger.rs @@ -12,9 +12,8 @@ //! The last part also means that some Unicode characters might not be //! supported by the UEFI console. Don't expect emoji output support. -use crate::proto::console::text::Output; - use crate::prelude::{Boot, SystemTable}; +use crate::proto::console::text::Output; use core::fmt::{self, Write}; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -43,6 +42,31 @@ pub fn disable() { LOGGER.disable(); } +/// Writer to the QEMU debugcon device and the debug-console of +/// cloud-hypervisor. +/// +/// More info: +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[derive(Copy, Clone, Debug)] +struct DebugconWriter; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl DebugconWriter { + const IO_PORT: u16 = 0xe9; +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +impl core::fmt::Write for DebugconWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + for &byte in s.as_bytes() { + unsafe { + core::arch::asm!("outb %al, %dx", in("al") byte, in("dx") DebugconWriter::IO_PORT, options(att_syntax)) + }; + } + Ok(()) + } +} + /// Logging implementation which writes to a UEFI output stream. /// /// If this logger is used as a global logger, you must disable it using the @@ -99,38 +123,40 @@ impl Logger { impl log::Log for Logger { fn enabled(&self, _metadata: &log::Metadata) -> bool { - !self.output().is_null() + // We decide in `log` already if something is printed. We do not + // need micro optimizations here. + true } fn log(&self, record: &log::Record) { - let output = self.output(); - - if !output.is_null() { - let writer = unsafe { &mut *output }; - let result = DecoratedLog::write( + if let Some(writer) = unsafe { self.output().as_mut() } { + // Ignore all errors. Since we're in the logger implementation we + // can't log the error. We also don't want to panic, since logging + // is generally not critical functionality. + let _ = DecoratedLog::write( writer, record.level(), record.args(), record.file().unwrap_or(""), record.line().unwrap_or(0), ); + } - // Some UEFI implementations, such as the one used by VirtualBox, - // may intermittently drop out some text from SimpleTextOutput and - // report an EFI_DEVICE_ERROR. This will be reported here as an - // `fmt::Error`, and given how the `log` crate is designed, our main - // choices when that happens are to ignore the error or panic. - // - // Ignoring errors is bad, especially when they represent loss of - // precious early-boot system diagnosis data, so we panic by - // default. But if you experience this problem and want your UEFI - // application to keep running when it happens, you can disable the - // `panic-on-logger-errors` cargo feature. If you do so, logging errors - // will be ignored by `uefi-rs` instead. - // - if cfg!(feature = "panic-on-logger-errors") { - result.unwrap() - } + #[cfg(all( + any(target_arch = "x86", target_arch = "x86_64"), + feature = "log-debugcon" + ))] + { + // Ignore all errors. Since we're in the logger implementation we + // can't log the error. We also don't want to panic, since logging + // is generally not critical functionality. + let _ = DecoratedLog::write( + &mut DebugconWriter, + record.level(), + record.args(), + record.file().unwrap_or(""), + record.line().unwrap_or(0), + ); } } diff --git a/uefi/src/helpers/mod.rs b/uefi/src/helpers/mod.rs index 0a757b615..e7afe9a6e 100644 --- a/uefi/src/helpers/mod.rs +++ b/uefi/src/helpers/mod.rs @@ -4,7 +4,10 @@ //! //! For now, this includes: //! - using [`uefi::allocator::Allocator`] as global allocator (feature `global_allocator`) -//! - an implementation of [`log::Log`] (feature `logger`) +//! - an implementation of [`log::Log`] (feature `logger`) which logs to +//! the stdout text protocol of UEFI (as long as boot services were not +//! excited) and to the [debugcon device](https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/) +//! (only on x86) (feature `log-debugcon`). //! - [`print!`][print_macro] and [`println!`][println_macro] macros defaulting //! to the uefi boot service stdout stream //! - default panic handler (feature `panic_handler`) @@ -16,14 +19,9 @@ //! [println_macro]: uefi::println! use crate::prelude::{Boot, SystemTable}; -use crate::Result; -use crate::StatusExt; -use core::ffi::c_void; -use core::ptr; -use core::sync::atomic::{AtomicPtr, Ordering}; +use crate::{table, Result}; #[doc(hidden)] pub use println::_print; -use uefi_raw::Status; #[cfg(feature = "global_allocator")] mod global_allocator; @@ -33,25 +31,6 @@ mod logger; mod panic_handler; mod println; -/// Reference to the system table. -/// -/// This table is only fully safe to use until UEFI boot services have been exited. -/// After that, some fields and methods are unsafe to use, see the documentation of -/// UEFI's ExitBootServices entry point for more details. -static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - -#[must_use] -fn system_table_opt() -> Option> { - let ptr = SYSTEM_TABLE.load(Ordering::Acquire); - // Safety: the `SYSTEM_TABLE` pointer either be null or a valid system - // table. - // - // Null is the initial value, as well as the value set when exiting boot - // services. Otherwise, the value is set by the call to `init`, which - // requires a valid system table reference as input. - unsafe { SystemTable::from_ptr(ptr) } -} - /// Obtains a pointer to the system table. /// /// This is meant to be used by higher-level libraries, @@ -61,9 +40,9 @@ fn system_table_opt() -> Option> { /// /// The returned pointer is only valid until boot services are exited. #[must_use] -// TODO do we want to keep this public? +#[deprecated(note = "use uefi::table::system_table_boot instead")] pub fn system_table() -> SystemTable { - system_table_opt().expect("The system table handle is not available") + table::system_table_boot().expect("boot services are not active") } /// Initialize all helpers defined in [`uefi::helpers`] whose Cargo features @@ -73,16 +52,10 @@ pub fn system_table() -> SystemTable { /// memory allocation capabilities. /// /// **PLEASE NOTE** that these helpers are meant for the pre exit boot service -/// epoch. +/// epoch. Limited functionality might work after exiting them, such as logging +/// to the debugcon device. +#[allow(unused_variables)] // `st` is unused if logger and allocator are disabled pub fn init(st: &mut SystemTable) -> Result<()> { - if system_table_opt().is_some() { - // Avoid double initialization. - return Status::SUCCESS.to_result_with_val(|| ()); - } - - // Setup the system table singleton - SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release); - // Setup logging and memory allocation #[cfg(feature = "logger")] @@ -92,24 +65,16 @@ pub fn init(st: &mut SystemTable) -> Result<()> { #[cfg(feature = "global_allocator")] unsafe { - uefi::allocator::init(st); + crate::allocator::init(st); } Ok(()) } pub(crate) fn exit() { - // DEBUG: The UEFI spec does not guarantee that this printout will work, as - // the services used by logging might already have been shut down. - // But it works on current OVMF, and can be used as a handy way to - // check that the callback does get called. - // - // info!("Shutting down the UEFI utility library"); - SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release); - #[cfg(feature = "logger")] logger::disable(); #[cfg(feature = "global_allocator")] - uefi::allocator::exit_boot_services(); + crate::allocator::exit_boot_services(); } diff --git a/uefi/src/helpers/panic_handler.rs b/uefi/src/helpers/panic_handler.rs index b70b33273..5918395b8 100644 --- a/uefi/src/helpers/panic_handler.rs +++ b/uefi/src/helpers/panic_handler.rs @@ -1,5 +1,5 @@ -use crate::helpers::system_table_opt; use crate::println; +use crate::table::system_table_boot; use cfg_if::cfg_if; #[panic_handler] @@ -7,7 +7,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { println!("[PANIC]: {}", info); // Give the user some time to read the message - if let Some(st) = system_table_opt() { + if let Some(st) = system_table_boot() { st.boot_services().stall(10_000_000); } else { let mut dummy = 0u64; @@ -28,10 +28,10 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { qemu_exit_handle.exit_failure(); } else { // If the system table is available, use UEFI's standard shutdown mechanism - if let Some(st) = system_table_opt() { - use uefi::table::runtime::ResetType; + if let Some(st) = system_table_boot() { + use crate::table::runtime::ResetType; st.runtime_services() - .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); + .reset(ResetType::SHUTDOWN, crate::Status::ABORTED, None); } // If we don't have any shutdown mechanism handy, the best we can do is loop diff --git a/uefi/src/helpers/println.rs b/uefi/src/helpers/println.rs index 265911da1..2a30eb1d5 100644 --- a/uefi/src/helpers/println.rs +++ b/uefi/src/helpers/println.rs @@ -1,10 +1,11 @@ -use crate::helpers::system_table; +use crate::table::system_table_boot; use core::fmt::Write; /// INTERNAL API! Helper for print macros. #[doc(hidden)] pub fn _print(args: core::fmt::Arguments) { - system_table() + system_table_boot() + .expect("boot services are not active") .stdout() .write_fmt(args) .expect("Failed to write to stdout"); diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index d715bfb55..fb658d7c3 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -48,7 +48,8 @@ //! the Rust standard library. For example, methods that return a //! `Vec` rather than filling a statically-sized array. This requires //! a global allocator; you can use the `global_allocator` feature or -//! provide your own. +//! provide your own. This is independent of internal direct usages of the +//! UEFI boot service allocator which may happen anyway, where necessary. //! - `global_allocator`: Set [`allocator::Allocator`] as the global Rust //! allocator. This is a simple allocator that relies on the UEFI pool //! allocator. You can choose to provide your own allocator instead of @@ -83,7 +84,6 @@ //! [spec]: https://uefi.org/specifications //! [unstable features]: https://doc.rust-lang.org/unstable-book/ -#![cfg_attr(feature = "unstable", feature(error_in_core))] #![cfg_attr(all(feature = "unstable", feature = "alloc"), feature(allocator_api))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![no_std] @@ -100,19 +100,24 @@ extern crate alloc; // see https://github.com/rust-lang/rust/issues/54647 extern crate self as uefi; +/// Re-export ucs2_cstr so that it can be used in the implementation of the +/// cstr16 macro. It is hidden since it's not intended to be used directly. +#[doc(hidden)] +pub use ucs2::ucs2_cstr; + #[macro_use] extern crate uefi_raw; #[macro_use] pub mod data_types; #[cfg(feature = "alloc")] -pub use self::data_types::CString16; -pub use self::data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify}; -pub use uefi_macros::{cstr16, cstr8, entry}; +pub use data_types::CString16; +pub use data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify}; +pub use uefi_macros::entry; pub use uguid::guid; mod result; -pub use self::result::{Error, Result, ResultExt, Status, StatusExt}; +pub use result::{Error, Result, ResultExt, Status, StatusExt}; pub mod table; @@ -133,25 +138,5 @@ pub(crate) mod polyfill; pub mod helpers; +mod macros; mod util; - -#[cfg(test)] -// Crates that create procedural macros can't unit test the macros they export. -// Therefore, we do some tests here. -mod macro_tests { - use uefi_macros::{cstr16, cstr8}; - - #[test] - fn cstr8_macro_literal() { - let _empty1 = cstr8!(); - let _empty2 = cstr8!(""); - let _regular = cstr8!("foobar"); - } - - #[test] - fn cstr16_macro_literal() { - let _empty1 = cstr16!(); - let _empty2 = cstr16!(""); - let _regular = cstr16!("foobar"); - } -} diff --git a/uefi/src/macros.rs b/uefi/src/macros.rs new file mode 100644 index 000000000..5bb2f7b79 --- /dev/null +++ b/uefi/src/macros.rs @@ -0,0 +1,80 @@ +/// Encode a string literal as a [`&CStr8`]. +/// +/// The encoding is done at compile time, so the result can be used in a +/// `const` item. +/// +/// An empty string containing just a null character can be created with either +/// `cstr8!()` or `cstr8!("")`. +/// +/// # Example +/// +/// ``` +/// use uefi::{CStr8, cstr8}; +/// +/// const S: &CStr8 = cstr8!("abÿ"); +/// assert_eq!(S.as_bytes(), [97, 98, 255, 0]); +/// +/// const EMPTY: &CStr8 = cstr8!(); +/// assert_eq!(EMPTY.as_bytes(), [0]); +/// assert_eq!(cstr8!(""), EMPTY); +/// ``` +/// +/// [`&CStr8`]: crate::CStr8 +#[macro_export] +macro_rules! cstr8 { + () => {{ + const S: &[u8] = &[0]; + // SAFETY: `S` is a trivially correct Latin-1 C string. + unsafe { $crate::CStr8::from_bytes_with_nul_unchecked(S) } + }}; + ($s:literal) => {{ + // Use `const` values here to force errors to happen at compile + // time. + + // Add one for the null char. + const NUM_CHARS: usize = $crate::data_types::str_num_latin1_chars($s) + 1; + + const VAL: [u8; NUM_CHARS] = $crate::data_types::str_to_latin1($s); + + // SAFETY: the `str_to_latin1` function always produces a valid Latin-1 + // string with a trailing null character. + unsafe { $crate::CStr8::from_bytes_with_nul_unchecked(&VAL) } + }}; +} + +/// Encode a string literal as a [`&CStr16`]. +/// +/// The encoding is done at compile time, so the result can be used in a +/// `const` item. +/// +/// An empty string containing just a null character can be created with either +/// `cstr16!()` or `cstr16!("")`. +/// +/// # Example +/// +/// ``` +/// use uefi::{CStr16, cstr16}; +/// +/// const S: &CStr16 = cstr16!("abc"); +/// assert_eq!(S.to_u16_slice_with_nul(), [97, 98, 99, 0]); +/// +/// const EMPTY: &CStr16 = cstr16!(); +/// assert_eq!(EMPTY.to_u16_slice_with_nul(), [0]); +/// assert_eq!(cstr16!(""), EMPTY); +/// ``` +/// +/// [`&CStr16`]: crate::CStr16 +#[macro_export] +macro_rules! cstr16 { + () => {{ + const S: &[u16] = &[0]; + // SAFETY: `S` is a trivially correct UCS-2 C string. + unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } + }}; + ($s:literal) => {{ + const S: &[u16] = &$crate::ucs2_cstr!($s); + // SAFETY: the ucs2_cstr macro always produces a valid UCS-2 string with + // a trailing null character. + unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } + }}; +} diff --git a/uefi/src/mem.rs b/uefi/src/mem.rs index bd6d3794a..1d0d66d41 100644 --- a/uefi/src/mem.rs +++ b/uefi/src/mem.rs @@ -23,10 +23,14 @@ use {core::alloc::Allocator, core::ptr::NonNull}; /// success. /// /// # Feature `unstable` / `allocator_api` -/// By default, this function works with Rust's default allocation mechanism. If you activate the -/// `unstable`-feature, it uses the `allocator_api` instead. In that case, the function takes an -/// additional parameter describing the specific [`Allocator`]. You can use [`alloc::alloc::Global`] -/// as default. +/// By default, this function works with the allocator that is set as +/// `#[global_allocator]`. This might be UEFI allocator but depends on your +/// use case and how you set up the environment. +/// +/// If you activate the `unstable`-feature, all allocations uses the provided +/// allocator (via `allocator_api`) instead. In that case, the function takes an +/// additional parameter describing the specific [`Allocator`]. You can use +/// [`alloc::alloc::Global`] which defaults to the `#[global_allocator]`. /// /// [`Allocator`]: https://doc.rust-lang.org/alloc/alloc/trait.Allocator.html /// [`alloc::alloc::Global`]: https://doc.rust-lang.org/alloc/alloc/struct.Global.html diff --git a/uefi/src/prelude.rs b/uefi/src/prelude.rs index 644acbe40..f32f9903c 100644 --- a/uefi/src/prelude.rs +++ b/uefi/src/prelude.rs @@ -2,12 +2,9 @@ //! //! This includes the system table types, `Status` codes, etc. -pub use crate::{Handle, ResultExt, Status, StatusExt}; +pub use crate::{cstr16, cstr8, entry, Handle, ResultExt, Status, StatusExt}; // Import the basic table types. pub use crate::table::boot::BootServices; pub use crate::table::runtime::RuntimeServices; pub use crate::table::{Boot, SystemTable}; - -// Import the macro for creating the custom entry point, as well as the cstr macros. -pub use uefi_macros::{cstr16, cstr8, entry}; diff --git a/uefi/src/proto/console/gop.rs b/uefi/src/proto/console/gop.rs index 351dc0c89..5ae81596d 100644 --- a/uefi/src/proto/console/gop.rs +++ b/uefi/src/proto/console/gop.rs @@ -76,7 +76,7 @@ pub struct GraphicsOutput(GraphicsOutputProtocol); impl GraphicsOutput { /// Returns information for an available graphics mode that the graphics /// device and the set of active video output devices supports. - pub fn query_mode(&self, index: u32, bs: &BootServices) -> Result { + fn query_mode(&self, index: u32, bs: &BootServices) -> Result { let mut info_sz = 0; let mut info_heap_ptr = ptr::null(); // query_mode allocates a buffer and stores the heap ptr in the provided @@ -100,7 +100,7 @@ impl GraphicsOutput { }) } - /// Returns information about all available graphics modes. + /// Returns a [`ModeIter`]. #[must_use] pub fn modes<'a>(&'a self, bs: &'a BootServices) -> ModeIter { ModeIter { @@ -336,7 +336,7 @@ pub enum PixelFormat { } /// Represents a graphics mode compatible with a given graphics device. -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub struct Mode { index: u32, info_sz: usize, diff --git a/uefi/src/proto/console/text/mod.rs b/uefi/src/proto/console/text/mod.rs index ebcd3fcec..089575fce 100644 --- a/uefi/src/proto/console/text/mod.rs +++ b/uefi/src/proto/console/text/mod.rs @@ -1,7 +1,7 @@ //! Text I/O. mod input; -pub use self::input::{Input, Key, ScanCode}; +pub use input::{Input, Key, ScanCode}; mod output; -pub use self::output::{Color, Output, OutputMode}; +pub use output::{Color, Output, OutputMode}; diff --git a/uefi/src/proto/debug/exception.rs b/uefi/src/proto/debug/exception.rs index ae3852711..4298a54b6 100644 --- a/uefi/src/proto/debug/exception.rs +++ b/uefi/src/proto/debug/exception.rs @@ -140,7 +140,7 @@ impl ExceptionType { pub const MAX_AARCH64_EXCEPTION: ExceptionType = ExceptionType::EXCEPT_AARCH64_SERROR; } -#[cfg(target_arch = "riscv")] +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] impl ExceptionType { /// Instruction misaligned pub const EXCEPT_RISCV_INST_MISALIGNED: ExceptionType = ExceptionType(0); diff --git a/uefi/src/proto/debug/mod.rs b/uefi/src/proto/debug/mod.rs index f626cb866..f2321889a 100644 --- a/uefi/src/proto/debug/mod.rs +++ b/uefi/src/proto/debug/mod.rs @@ -15,8 +15,8 @@ use crate::proto::unsafe_protocol; use crate::{Result, Status, StatusExt}; // re-export for ease of use -pub use self::context::SystemContext; -pub use self::exception::ExceptionType; +pub use context::SystemContext; +pub use exception::ExceptionType; mod context; mod exception; diff --git a/uefi/src/proto/device_path/mod.rs b/uefi/src/proto/device_path/mod.rs index 658d84c9d..185a4e447 100644 --- a/uefi/src/proto/device_path/mod.rs +++ b/uefi/src/proto/device_path/mod.rs @@ -119,6 +119,18 @@ pub struct DevicePathHeader { pub length: u16, } +impl<'a> TryFrom<&[u8]> for &'a DevicePathHeader { + type Error = ByteConversionError; + + fn try_from(bytes: &[u8]) -> Result { + if mem::size_of::() <= bytes.len() { + unsafe { Ok(&*bytes.as_ptr().cast::()) } + } else { + Err(ByteConversionError::InvalidLength) + } + } +} + /// A single node within a [`DevicePath`]. /// /// Each node starts with a [`DevicePathHeader`]. The rest of the data @@ -253,6 +265,19 @@ impl PartialEq for DevicePathNode { } } +impl<'a> TryFrom<&[u8]> for &'a DevicePathNode { + type Error = ByteConversionError; + + fn try_from(bytes: &[u8]) -> Result { + let dp = <&DevicePathHeader>::try_from(bytes)?; + if usize::from(dp.length) <= bytes.len() { + unsafe { Ok(DevicePathNode::from_ffi_ptr(bytes.as_ptr().cast())) } + } else { + Err(ByteConversionError::InvalidLength) + } + } +} + /// A single device path instance that ends with either an [`END_INSTANCE`] /// or [`END_ENTIRE`] node. Use [`DevicePath::instance_iter`] to get the /// path instances in a [`DevicePath`]. @@ -371,6 +396,35 @@ impl DevicePath { total_size_in_bytes } + /// Calculate the size in bytes of the entire `DevicePath` starting + /// at `bytes`. This adds up each node's length, including the + /// end-entire node. + /// + /// # Errors + /// + /// The [`ByteConversionError::InvalidLength`] error will be returned + /// when the length of the given bytes slice cannot contain the full + /// [`DevicePath`] represented by the slice. + fn size_in_bytes_from_slice(mut bytes: &[u8]) -> Result { + let max_size_in_bytes = bytes.len(); + let mut total_size_in_bytes: usize = 0; + loop { + let node = <&DevicePathNode>::try_from(bytes)?; + let node_size_in_bytes = usize::from(node.length()); + total_size_in_bytes += node_size_in_bytes; + // Length of last processed node extends past the bytes slice. + if total_size_in_bytes > max_size_in_bytes { + return Err(ByteConversionError::InvalidLength); + } + if node.is_end_entire() { + break; + } + bytes = &bytes[node_size_in_bytes..]; + } + + Ok(total_size_in_bytes) + } + /// Create a [`DevicePath`] reference from an opaque pointer. /// /// # Safety @@ -462,6 +516,15 @@ impl PartialEq for DevicePath { } } +impl<'a> TryFrom<&[u8]> for &'a DevicePath { + type Error = ByteConversionError; + + fn try_from(bytes: &[u8]) -> Result { + let len = DevicePath::size_in_bytes_from_slice(bytes)?; + unsafe { Ok(&*ptr_meta::from_raw_parts(bytes.as_ptr().cast(), len)) } + } +} + #[cfg(feature = "alloc")] impl ToOwned for DevicePath { type Owned = Box; @@ -729,6 +792,14 @@ impl DeviceSubType { pub const END_ENTIRE: DeviceSubType = DeviceSubType(0xff); } +/// Error returned when attempting to convert from a `&[u8]` to a +/// [`DevicePath`] type. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ByteConversionError { + /// The length of the given slice is not valid for its [`DevicePath`] type. + InvalidLength, +} + /// Error returned when converting from a [`DevicePathNode`] to a more /// specific node type. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -954,4 +1025,57 @@ mod tests { let owned_dp_ref = &*owned_dp; assert_eq!(owned_dp_ref, dp) } + + #[test] + fn test_device_path_node_from_bytes() { + let mut raw_data = Vec::new(); + let node = [0xa0, 0xb0]; + let node_data = &[10, 11]; + + // Raw data is less than size of a [`DevicePathNode`]. + raw_data.push(node[0]); + assert!(<&DevicePathNode>::try_from(raw_data.as_slice()).is_err()); + + // Raw data is long enough to hold a [`DevicePathNode`]. + raw_data.push(node[1]); + raw_data.extend( + u16::try_from(mem::size_of::() + node_data.len()) + .unwrap() + .to_le_bytes(), + ); + raw_data.extend(node_data); + let dp = <&DevicePathNode>::try_from(raw_data.as_slice()).unwrap(); + + // Relevant assertions to verify the conversion is fine. + assert_eq!(mem::size_of_val(dp), 6); + check_node(dp, 0xa0, 0xb0, &[10, 11]); + + // [`DevicePathNode`] data length exceeds the raw_data slice. + raw_data[2] += 1; + assert!(<&DevicePathNode>::try_from(raw_data.as_slice()).is_err()); + } + + #[test] + fn test_device_path_nodes_from_bytes() { + let raw_data = create_raw_device_path(); + let dp = <&DevicePath>::try_from(raw_data.as_slice()).unwrap(); + + // Check that the size is the sum of the nodes' lengths. + assert_eq!(mem::size_of_val(dp), 6 + 8 + 4 + 6 + 8 + 4); + + // Check the list's node iter. + let nodes: Vec<_> = dp.node_iter().collect(); + check_node(nodes[0], 0xa0, 0xb0, &[10, 11]); + check_node(nodes[1], 0xa1, 0xb1, &[20, 21, 22, 23]); + check_node( + nodes[2], + DeviceType::END.0, + DeviceSubType::END_INSTANCE.0, + &[], + ); + check_node(nodes[3], 0xa2, 0xb2, &[30, 31]); + check_node(nodes[4], 0xa3, 0xb3, &[40, 41, 42, 43]); + // The end-entire node is not returned by the iterator. + assert_eq!(nodes.len(), 5); + } } diff --git a/uefi/src/proto/media/file/mod.rs b/uefi/src/proto/media/file/mod.rs index 2628e0443..bbb6d365d 100644 --- a/uefi/src/proto/media/file/mod.rs +++ b/uefi/src/proto/media/file/mod.rs @@ -15,17 +15,19 @@ use core::ffi::c_void; use core::fmt::Debug; use core::{mem, ptr}; use uefi_raw::protocol::file_system::FileProtocolV1; + #[cfg(all(feature = "unstable", feature = "alloc"))] use {alloc::alloc::Global, core::alloc::Allocator}; + #[cfg(feature = "alloc")] -use {alloc::boxed::Box, uefi::mem::make_boxed}; +use {crate::mem::make_boxed, alloc::boxed::Box}; -pub use self::dir::Directory; -pub use self::info::{ +pub use dir::Directory; +pub use info::{ FileInfo, FileInfoCreationError, FileProtocolInfo, FileSystemInfo, FileSystemVolumeLabel, FromUefi, }; -pub use self::regular::RegularFile; +pub use regular::RegularFile; pub use uefi_raw::protocol::file_system::FileAttribute; /// Common interface to `FileHandle`, `RegularFile`, and `Directory`. diff --git a/uefi/src/proto/misc.rs b/uefi/src/proto/misc.rs index 339c8ea6e..92666fd08 100644 --- a/uefi/src/proto/misc.rs +++ b/uefi/src/proto/misc.rs @@ -1,10 +1,16 @@ //! Miscellaneous protocols. +use uefi_raw::protocol::misc::{ + ResetNotificationProtocol, ResetSystemFn, TimestampProperties, TimestampProtocol, +}; + use crate::proto::unsafe_protocol; use crate::{Result, StatusExt}; -use uefi_raw::protocol::misc::{TimestampProperties, TimestampProtocol}; /// Protocol for retrieving a high-resolution timestamp counter. +/// **Note:** +/// If your UEFI firmware not support timestamp protocol which first added at UEFI spec 2.4 2013. +/// you also could use `RDTSC` in rust, here is a demo [Slint-UI](https://github.com/slint-ui/slint/blob/2c0ba2bc0f151eba8d1fa17839fa2ac58832ca80/examples/uefi-demo/main.rs#L28-L62) who use uefi-rs. #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(TimestampProtocol::GUID)] @@ -23,3 +29,55 @@ impl Timestamp { unsafe { (self.0.get_properties)(&mut properties) }.to_result_with_val(|| properties) } } + +/// Protocol to register for a notification when ResetSystem is called. +#[derive(Debug)] +#[repr(transparent)] +#[unsafe_protocol(ResetNotificationProtocol::GUID)] +pub struct ResetNotification(ResetNotificationProtocol); + +impl ResetNotification { + /// Register a notification function to be called when ResetSystem() is called. + /// + /// + /// # Example + /// + /// ```rust + /// use log::info; + /// use uefi::Handle; + /// use uefi::prelude::BootServices; + /// use uefi::proto::misc::{ResetNotification}; + /// use uefi_raw::Status; + /// use uefi_raw::table::runtime; + /// + /// + /// // value efi_reset_fn is the type of ResetSystemFn, a function pointer + /// unsafe extern "efiapi" fn efi_reset_fn( + /// rt: runtime::ResetType, + /// status: Status, + /// data_size: usize, + /// data: *const u8, + /// ){ + /// info!("Inside the event callback"); + /// info!("do what you want"); + /// } + /// + /// pub fn test(image: Handle, bt: &BootServices) { + /// + /// let mut rn = bt + /// .open_protocol_exclusive::(image) + /// .expect("Failed to open Timestamp protocol"); + /// + /// rn.register_reset_notify(efi_reset_fn) + /// .expect("Failed to register a reset notification function!"); + /// } + /// ``` + pub fn register_reset_notify(&mut self, reset_function: ResetSystemFn) -> Result { + unsafe { (self.0.register_reset_notify)(&mut self.0, reset_function) }.to_result() + } + + /// Remove a reset notification function that was previously registered with [`ResetNotification::register_reset_notify`]. + pub fn unregister_reset_notify(&mut self, reset_function: ResetSystemFn) -> Result { + unsafe { (self.0.unregister_reset_notify)(&mut self.0, reset_function) }.to_result() + } +} diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index d8a5f4bb6..ee9c2a999 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -9,12 +9,12 @@ use super::{IpAddress, MacAddress}; use crate::data_types::Event; +use crate::proto::unsafe_protocol; use crate::{Result, Status, StatusExt}; use bitflags::bitflags; use core::ffi::c_void; use core::ptr; use core::ptr::NonNull; -use uefi_macros::unsafe_protocol; /// The Simple Network Protocol #[derive(Debug)] diff --git a/uefi/src/result/mod.rs b/uefi/src/result/mod.rs index 5afc9b7df..4826c2f0c 100644 --- a/uefi/src/result/mod.rs +++ b/uefi/src/result/mod.rs @@ -4,11 +4,11 @@ use core::fmt::Debug; /// The error type that we use, essentially a status code + optional additional data mod error; -pub use self::error::Error; +pub use error::Error; /// Definition of UEFI's standard status codes mod status; -pub use self::status::{Status, StatusExt}; +pub use status::{Status, StatusExt}; /// Return type of most UEFI functions. Both success and error payloads are optional. /// diff --git a/uefi/src/table/boot.rs b/uefi/src/table/boot.rs index 6dbfb0bd1..1a27de37c 100644 --- a/uefi/src/table/boot.rs +++ b/uefi/src/table/boot.rs @@ -1,6 +1,6 @@ //! UEFI services available during boot. -use super::Revision; +use super::{system_table_boot, Revision}; use crate::data_types::{Align, PhysicalAddress}; use crate::proto::device_path::DevicePath; use crate::proto::loaded_image::LoadedImage; @@ -95,9 +95,9 @@ impl BootServices { /// Update the global image [`Handle`]. /// /// This is called automatically in the `main` entry point as part - /// of [`uefi_macros::entry`]. It should not be called at any other + /// of [`uefi::entry`]. It should not be called at any other /// point in time, unless the executable does not use - /// [`uefi_macros::entry`], in which case it should be called once + /// [`uefi::entry`], in which case it should be called once /// before calling other `BootServices` functions. /// /// # Safety @@ -179,62 +179,95 @@ impl BootServices { unsafe { (self.0.free_pages)(addr, count) }.to_result() } - /// Returns struct which contains the size of a single memory descriptor - /// as well as the size of the current memory map. + /// Queries the `get_memory_map` function of UEFI to retrieve the current + /// size of the map. Returns a [`MemoryMapMeta`]. /// - /// Note that the size of the memory map can increase any time an allocation happens, - /// so when creating a buffer to put the memory map into, it's recommended to allocate a few extra - /// elements worth of space above the size of the current memory map. + /// It is recommended to add a few more bytes for a subsequent allocation + /// for the memory map, as the memory map itself also needs heap memory, + /// and other allocations might occur before that call. #[must_use] - pub fn memory_map_size(&self) -> MemoryMapSize { + fn memory_map_size(&self) -> MemoryMapMeta { let mut map_size = 0; let mut map_key = MemoryMapKey(0); - let mut entry_size = 0; - let mut entry_version = 0; + let mut desc_size = 0; + let mut desc_version = 0; let status = unsafe { (self.0.get_memory_map)( &mut map_size, ptr::null_mut(), &mut map_key.0, - &mut entry_size, - &mut entry_version, + &mut desc_size, + &mut desc_version, ) }; assert_eq!(status, Status::BUFFER_TOO_SMALL); - MemoryMapSize { - entry_size, + assert_eq!( + map_size % desc_size, + 0, + "Memory map must be a multiple of the reported descriptor size." + ); + + let mmm = MemoryMapMeta { + desc_size, map_size, - } + map_key, + desc_version, + }; + + mmm.assert_sanity_checks(); + + mmm } - /// Retrieves the current memory map. - /// - /// The allocated buffer should be big enough to contain the memory map, - /// and a way of estimating how big it should be is by calling `memory_map_size`. + /// Stores the current UEFI memory map in an UEFI-heap allocated buffer + /// and returns a [`MemoryMap`]. /// - /// The buffer must be aligned like a `MemoryDescriptor`. + /// # Parameters /// - /// The returned key is a unique identifier of the current configuration of memory. - /// Any allocations or such will change the memory map's key. - /// - /// If you want to store the resulting memory map without having to keep - /// the buffer around, you can use `.copied().collect()` on the iterator. + /// - `mt`: The memory type for the backing memory on the UEFI heap. + /// Usually, this is [`MemoryType::LOADER_DATA`]. You can also use a + /// custom type. /// /// # Errors /// - /// See section `EFI_BOOT_SERVICES.GetMemoryMap()` in the UEFI Specification for more details. + /// See section `EFI_BOOT_SERVICES.GetMemoryMap()` in the UEFI Specification + /// for more details. /// /// * [`uefi::Status::BUFFER_TOO_SMALL`] /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result> { - let mut map_size = buffer.len(); - MemoryDescriptor::assert_aligned(buffer); - let map_buffer = buffer.as_mut_ptr().cast::(); + pub fn memory_map(&self, mt: MemoryType) -> Result { + let mut buffer = MemoryMapBackingMemory::new(mt)?; + + let meta = self.get_memory_map(buffer.as_mut_slice())?; + let MemoryMapMeta { + map_size, + map_key, + desc_size, + desc_version, + } = meta; + + let len = map_size / desc_size; + assert_eq!(map_size % desc_size, 0); + assert_eq!(desc_version, MemoryDescriptor::VERSION); + Ok(MemoryMap { + key: map_key, + buf: buffer, + meta, + len, + }) + } + + /// Calls the underlying `GetMemoryMap` function of UEFI. On success, + /// the buffer is mutated and contains the map. The map might be shorter + /// than the buffer, which is reflected by the return value. + pub(crate) fn get_memory_map(&self, buf: &mut [u8]) -> Result { + let mut map_size = buf.len(); + let map_buffer = buf.as_mut_ptr().cast::(); let mut map_key = MemoryMapKey(0); - let mut entry_size = 0; - let mut entry_version = 0; + let mut desc_size = 0; + let mut desc_version = 0; assert_eq!( (map_buffer as usize) % mem::align_of::(), @@ -247,19 +280,15 @@ impl BootServices { &mut map_size, map_buffer, &mut map_key.0, - &mut entry_size, - &mut entry_version, + &mut desc_size, + &mut desc_version, ) } - .to_result_with_val(move || { - let len = map_size / entry_size; - - MemoryMap { - key: map_key, - buf: buffer, - entry_size, - len, - } + .to_result_with_val(|| MemoryMapMeta { + map_size, + desc_size, + map_key, + desc_version, }) } @@ -271,9 +300,13 @@ impl BootServices { /// /// * [`uefi::Status::OUT_OF_RESOURCES`] /// * [`uefi::Status::INVALID_PARAMETER`] - pub fn allocate_pool(&self, mem_ty: MemoryType, size: usize) -> Result<*mut u8> { + pub fn allocate_pool(&self, mem_ty: MemoryType, size: usize) -> Result> { let mut buffer = ptr::null_mut(); - unsafe { (self.0.allocate_pool)(mem_ty, size, &mut buffer) }.to_result_with_val(|| buffer) + let ptr = unsafe { (self.0.allocate_pool)(mem_ty, size, &mut buffer) } + .to_result_with_val(|| buffer)?; + + Ok(NonNull::new(ptr) + .expect("UEFI should return error if an allocation failed but never a null pointer")) } /// Frees memory allocated from a pool. @@ -1604,48 +1637,226 @@ impl Align for MemoryDescriptor { #[repr(C)] pub struct MemoryMapKey(usize); -/// A structure containing the size of a memory descriptor and the size of the -/// memory map. +/// The backing memory for the UEFI memory app on the UEFI heap, allocated using +/// the UEFI boot services allocator. This occupied memory will also be +/// reflected in the memory map itself. +/// +/// Although untyped, it is similar to the `Box` type in terms of heap +/// allocation and deallocation, as well as ownership of the corresponding +/// memory. Apart from that, this type only has the semantics of a buffer. +/// +/// The memory is untyped, which is necessary due to the nature of the UEFI +/// spec. It still ensures a correct alignment to hold [`MemoryDescriptor`]. The +/// size of the buffer is sufficient to hold the memory map at the point in time +/// where this is created. Note that due to (not obvious or asynchronous) +/// allocations/deallocations in your environment, this might be outdated at the +/// time you store the memory map in it. +/// +/// Note that due to the nature of the UEFI memory app, this buffer might +/// hold (a few) bytes more than necessary. The `map_size` reported by +/// `get_memory_map` tells the actual size. +/// +/// When this type is dropped and boot services are not exited yet, the memory +/// is freed. +/// +/// # Usage +/// The type is intended to be used like this: +/// 1. create it using [`MemoryMapBackingMemory::new`] +/// 2. pass it to [`BootServices::get_memory_map`] +/// 3. construct a [`MemoryMap`] from it #[derive(Debug)] -pub struct MemoryMapSize { - /// Size of a single memory descriptor in bytes - pub entry_size: usize, - /// Size of the entire memory map in bytes +#[allow(clippy::len_without_is_empty)] // this type is never empty +pub(crate) struct MemoryMapBackingMemory(NonNull<[u8]>); + +impl MemoryMapBackingMemory { + /// Constructs a new [`MemoryMapBackingMemory`]. + /// + /// # Parameters + /// - `memory_type`: The memory type for the memory map allocation. + /// Typically, [`MemoryType::LOADER_DATA`] for regular UEFI applications. + pub(crate) fn new(memory_type: MemoryType) -> Result { + let st = system_table_boot().expect("Should have boot services activated"); + let bs = st.boot_services(); + + let memory_map_meta = bs.memory_map_size(); + let len = Self::safe_allocation_size_hint(memory_map_meta); + let ptr = bs.allocate_pool(memory_type, len)?.as_ptr(); + + // Should be fine as UEFI always has allocations with a guaranteed + // alignment of 8 bytes. + assert_eq!(ptr.align_offset(mem::align_of::()), 0); + + // If this panics, the UEFI implementation is broken. + assert_eq!(memory_map_meta.map_size % memory_map_meta.desc_size, 0); + + unsafe { Ok(Self::from_raw(ptr, len)) } + } + + unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self { + assert_eq!(ptr.align_offset(mem::align_of::()), 0); + + let ptr = NonNull::new(ptr).expect("UEFI should never return a null ptr. An error should have been reflected via an Err earlier."); + let slice = NonNull::slice_from_raw_parts(ptr, len); + + Self(slice) + } + + /// Creates an instance from the provided memory, which is not necessarily + /// on the UEFI heap. + #[cfg(test)] + fn from_slice(buffer: &mut [u8]) -> Self { + let len = buffer.len(); + unsafe { Self::from_raw(buffer.as_mut_ptr(), len) } + } + + /// Returns a "safe" best-effort size hint for the memory map size with + /// some additional bytes in buffer compared to the [`MemoryMapMeta`]. + /// This helps + #[must_use] + fn safe_allocation_size_hint(mmm: MemoryMapMeta) -> usize { + // Allocate space for extra entries beyond the current size of the + // memory map. The value of 8 matches the value in the Linux kernel: + // https://github.com/torvalds/linux/blob/e544a07438/drivers/firmware/efi/libstub/efistub.h#L173 + const EXTRA_ENTRIES: usize = 8; + + let extra_size = mmm.desc_size * EXTRA_ENTRIES; + mmm.map_size + extra_size + } + + /// Returns a raw pointer to the beginning of the allocation. + #[must_use] + pub fn as_ptr(&self) -> *const u8 { + self.0.as_ptr().cast() + } + + /// Returns a mutable raw pointer to the beginning of the allocation. + #[must_use] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.0.as_ptr().cast() + } + + /// Returns a slice to the underlying memory. + #[must_use] + pub fn as_slice(&self) -> &[u8] { + unsafe { self.0.as_ref() } + } + + /// Returns a mutable slice to the underlying memory. + #[must_use] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { self.0.as_mut() } + } +} + +// Don't drop when we use this in unit tests. +#[cfg(not(test))] +impl Drop for MemoryMapBackingMemory { + fn drop(&mut self) { + if let Some(bs) = system_table_boot() { + let res = unsafe { bs.boot_services().free_pool(self.0.as_ptr().cast()) }; + if let Err(e) = res { + log::error!("Failed to deallocate memory map: {e:?}"); + } + } else { + log::debug!("Boot services are excited. Memory map won't be freed using the UEFI boot services allocator."); + } + } +} + +/// A structure containing the meta attributes associated with a call to +/// `GetMemoryMap` of UEFI. Note that all values refer to the time this was +/// called. All following invocations (hidden, subtle, and asynchronous ones) +/// will likely invalidate this. +#[derive(Copy, Clone, Debug)] +pub struct MemoryMapMeta { + /// The actual size of the map. pub map_size: usize, + /// The reported memory descriptor size. Note that this is the reference + /// and never `size_of::()`! + pub desc_size: usize, + /// A unique memory key bound to a specific memory map version/state. + pub map_key: MemoryMapKey, + /// The version of the descriptor struct. + pub desc_version: u32, +} + +impl MemoryMapMeta { + /// Returns the amount of entries in the map. + #[must_use] + pub fn entry_count(&self) -> usize { + assert_eq!(self.map_size % self.desc_size, 0); + self.map_size / self.desc_size + } + + /// Runs some sanity assertions. + pub fn assert_sanity_checks(&self) { + assert!(self.desc_size > 0); + // Although very unlikely, this might fail if the memory descriptor is + // extended by a future UEFI revision by a significant amount, we + // update the struct, but an old UEFI implementation reports a small + // size. + assert!(self.desc_size >= mem::size_of::()); + assert!(self.map_size > 0); + + // Ensure the mmap size is (somehow) sane. + const ONE_GB: usize = 1024 * 1024 * 1024; + assert!(self.map_size <= ONE_GB); + } } /// An accessory to the memory map that can be either iterated or /// indexed like an array. /// -/// A [`MemoryMap`] is always associated with the -/// unique [`MemoryMapKey`] contained in the struct. +/// A [`MemoryMap`] is always associated with the unique [`MemoryMapKey`] +/// contained in the struct. /// /// To iterate over the entries, call [`MemoryMap::entries`]. To get a sorted /// map, you manually have to call [`MemoryMap::sort`] first. +/// +/// ## UEFI pitfalls +/// **Please note** that when working with memory maps, the `entry_size` is +/// usually larger than `size_of:: { +pub struct MemoryMap { + /// Backing memory, properly initialized at this point. + buf: MemoryMapBackingMemory, key: MemoryMapKey, - buf: &'buf mut [u8], - entry_size: usize, + meta: MemoryMapMeta, len: usize, } -impl<'buf> MemoryMap<'buf> { - /// Creates a [`MemoryMap`] from the given buffer and entry size. - /// - /// This allows parsing a memory map provided by a kernel after boot - /// services have already exited. - pub fn from_raw(buf: &'buf mut [u8], entry_size: usize) -> Self { - assert!(entry_size >= mem::size_of::()); - let len = buf.len() / entry_size; +impl MemoryMap { + /// Creates a [`MemoryMap`] from the give initialized memory map behind + /// the buffer and the reported `desc_size` from UEFI. + pub(crate) fn from_initialized_mem(buf: MemoryMapBackingMemory, meta: MemoryMapMeta) -> Self { + assert!(meta.desc_size >= mem::size_of::()); + let len = meta.entry_count(); MemoryMap { key: MemoryMapKey(0), buf, - entry_size, + meta, len, } } + #[cfg(test)] + fn from_raw(buf: &mut [u8], desc_size: usize) -> Self { + let mem = MemoryMapBackingMemory::from_slice(buf); + Self::from_initialized_mem( + mem, + MemoryMapMeta { + map_size: buf.len(), + desc_size, + map_key: MemoryMapKey(0), + desc_version: MemoryDescriptor::VERSION, + }, + ) + } + #[must_use] /// Returns the unique [`MemoryMapKey`] associated with the memory map. pub fn key(&self) -> MemoryMapKey { @@ -1709,21 +1920,27 @@ impl<'buf> MemoryMap<'buf> { unsafe { ptr::swap_nonoverlapping( - base.add(index1 * self.entry_size), - base.add(index2 * self.entry_size), - self.entry_size, + base.add(index1 * self.meta.desc_size), + base.add(index2 * self.meta.desc_size), + self.meta.desc_size, ); } } fn get_element_phys_addr(&self, index: usize) -> PhysicalAddress { - let offset = index.checked_mul(self.entry_size).unwrap(); + let offset = index.checked_mul(self.meta.desc_size).unwrap(); let elem = unsafe { &*self.buf.as_ptr().add(offset).cast::() }; elem.phys_start } - /// Returns an iterator over the contained memory map. To get a sorted map, - /// call [`MemoryMap::sort`] first. + /// Returns an [`MemoryMapIter`] emitting [`MemoryDescriptor`]s. + /// + /// To get a sorted map, call [`MemoryMap::sort`] first. + /// + /// # UEFI pitfalls + /// Currently, only the descriptor version specified in + /// [`MemoryDescriptor`] is supported. This is going to change if the UEFI + /// spec ever introduces a new memory descriptor version. #[must_use] pub fn entries(&self) -> MemoryMapIter { MemoryMapIter { @@ -1734,7 +1951,7 @@ impl<'buf> MemoryMap<'buf> { /// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds. #[must_use] - pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> { + pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> { if index >= self.len { return None; } @@ -1743,7 +1960,7 @@ impl<'buf> MemoryMap<'buf> { &*self .buf .as_ptr() - .add(self.entry_size * index) + .add(self.meta.desc_size * index) .cast::() }; @@ -1752,7 +1969,7 @@ impl<'buf> MemoryMap<'buf> { /// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds. #[must_use] - pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> { + pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> { if index >= self.len { return None; } @@ -1761,15 +1978,24 @@ impl<'buf> MemoryMap<'buf> { &mut *self .buf .as_mut_ptr() - .add(self.entry_size * index) + .add(self.meta.desc_size * index) .cast::() }; Some(desc) } + + /// Provides access to the raw memory map. + /// + /// This is for example useful if you want to embed the memory map into + /// another data structure, such as a Multiboot2 boot information. + #[must_use] + pub fn as_raw(&self) -> (&[u8], MemoryMapMeta) { + (self.buf.as_slice(), self.meta) + } } -impl core::ops::Index for MemoryMap<'_> { +impl core::ops::Index for MemoryMap { type Output = MemoryDescriptor; fn index(&self, index: usize) -> &Self::Output { @@ -1777,7 +2003,7 @@ impl core::ops::Index for MemoryMap<'_> { } } -impl core::ops::IndexMut for MemoryMap<'_> { +impl core::ops::IndexMut for MemoryMap { fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.get_mut(index).unwrap() } @@ -1786,13 +2012,13 @@ impl core::ops::IndexMut for MemoryMap<'_> { /// An iterator of [`MemoryDescriptor`]. The underlying memory map is always /// associated with a unique [`MemoryMapKey`]. #[derive(Debug, Clone)] -pub struct MemoryMapIter<'buf> { - memory_map: &'buf MemoryMap<'buf>, +pub struct MemoryMapIter<'a> { + memory_map: &'a MemoryMap, index: usize, } -impl<'buf> Iterator for MemoryMapIter<'buf> { - type Item = &'buf MemoryDescriptor; +impl<'a> Iterator for MemoryMapIter<'a> { + type Item = &'a MemoryDescriptor; fn size_hint(&self) -> (usize, Option) { let sz = self.memory_map.len - self.index; @@ -1941,7 +2167,7 @@ impl<'a> HandleBuffer<'a> { pub struct ProtocolSearchKey(NonNull); #[cfg(test)] -mod tests { +mod tests_mmap_artificial { use core::mem::{size_of, size_of_val}; use crate::table::boot::{MemoryAttribute, MemoryMap, MemoryType}; @@ -2043,7 +2269,7 @@ mod tests { } // Added for debug purposes on test failure - impl core::fmt::Display for MemoryMap<'_> { + impl core::fmt::Display for MemoryMap { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { writeln!(f)?; for desc in self.entries() { @@ -2072,3 +2298,138 @@ mod tests { true } } + +#[cfg(test)] +mod tests_mmap_real { + use super::*; + use core::mem::size_of; + + const MMAP_META: MemoryMapMeta = MemoryMapMeta { + map_size: MMAP_RAW.len() * size_of::(), + desc_size: 48, + map_key: MemoryMapKey(0), + desc_version: 1, + }; + /// Sample with 10 entries of a real UEFI memory map extracted from our + /// UEFI test runner. + const MMAP_RAW: [u64; 60] = [ + 3, 0, 0, 1, 15, 0, 7, 4096, 0, 134, 15, 0, 4, 552960, 0, 1, 15, 0, 7, 557056, 0, 24, 15, 0, + 7, 1048576, 0, 1792, 15, 0, 10, 8388608, 0, 8, 15, 0, 7, 8421376, 0, 3, 15, 0, 10, 8433664, + 0, 1, 15, 0, 7, 8437760, 0, 4, 15, 0, 10, 8454144, 0, 240, 15, 0, + ]; + extern crate std; + #[test] + fn basic_functionality() { + let mut buf = MMAP_RAW; + let buf = + unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr().cast::(), MMAP_META.map_size) }; + let mut mmap = MemoryMap::from_raw(buf, MMAP_META.desc_size); + mmap.sort(); + + let entries = mmap.entries().copied().collect::>(); + + let expected = [ + MemoryDescriptor { + ty: MemoryType::BOOT_SERVICES_CODE, + phys_start: 0x0, + virt_start: 0x0, + page_count: 0x1, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::CONVENTIONAL, + phys_start: 0x1000, + virt_start: 0x0, + page_count: 0x86, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::BOOT_SERVICES_DATA, + phys_start: 0x87000, + virt_start: 0x0, + page_count: 0x1, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::CONVENTIONAL, + phys_start: 0x88000, + virt_start: 0x0, + page_count: 0x18, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::CONVENTIONAL, + phys_start: 0x100000, + virt_start: 0x0, + page_count: 0x700, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::ACPI_NON_VOLATILE, + phys_start: 0x800000, + virt_start: 0x0, + page_count: 0x8, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::CONVENTIONAL, + phys_start: 0x808000, + virt_start: 0x0, + page_count: 0x3, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::ACPI_NON_VOLATILE, + phys_start: 0x80b000, + virt_start: 0x0, + page_count: 0x1, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::CONVENTIONAL, + phys_start: 0x80c000, + virt_start: 0x0, + page_count: 0x4, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + MemoryDescriptor { + ty: MemoryType::ACPI_NON_VOLATILE, + phys_start: 0x810000, + virt_start: 0x0, + page_count: 0xf0, + att: MemoryAttribute::UNCACHEABLE + | MemoryAttribute::WRITE_COMBINE + | MemoryAttribute::WRITE_THROUGH + | MemoryAttribute::WRITE_BACK, + }, + ]; + assert_eq!(entries.as_slice(), &expected); + } +} diff --git a/uefi/src/table/mod.rs b/uefi/src/table/mod.rs index 8c57aba3b..078c0f79f 100644 --- a/uefi/src/table/mod.rs +++ b/uefi/src/table/mod.rs @@ -1,21 +1,75 @@ //! Standard UEFI tables. -/// Common trait implemented by all standard UEFI tables. -pub trait Table { - /// A unique number assigned by the UEFI specification - /// to the standard tables. - const SIGNATURE: u64; -} +pub mod boot; +pub mod cfg; +pub mod runtime; mod header; -pub use self::header::Header; - mod system; -pub use self::system::{Boot, Runtime, SystemTable}; -pub mod boot; -pub mod runtime; +pub use header::Header; +pub use system::{Boot, Runtime, SystemTable}; +pub use uefi_raw::table::Revision; -pub mod cfg; +use core::ptr; +use core::sync::atomic::{AtomicPtr, Ordering}; -pub use uefi_raw::table::Revision; +/// Global system table pointer. This is only modified by [`set_system_table`]. +static SYSTEM_TABLE: AtomicPtr = + AtomicPtr::new(ptr::null_mut()); + +/// Update the global system table pointer. +/// +/// This is called automatically in the `main` entry point as part of +/// [`uefi::entry`]. It should not be called at any other point in time, unless +/// the executable does not use [`uefi::entry`], in which case it should be +/// called once before calling any other API in this crate. +/// +/// # Safety +/// +/// This function should only be called as described above, and the +/// `ptr` must be a valid [`SystemTable`]. +pub unsafe fn set_system_table(ptr: *const uefi_raw::table::system::SystemTable) { + SYSTEM_TABLE.store(ptr.cast_mut(), Ordering::Release); +} + +/// Get the system table while boot services are active. +pub fn system_table_boot() -> Option> { + let st = SYSTEM_TABLE.load(Ordering::Acquire); + if st.is_null() { + return None; + } + + // SAFETY: the system table is valid per the requirements of `set_system_table`. + unsafe { + if (*st).boot_services.is_null() { + None + } else { + Some(SystemTable::::from_ptr(st.cast()).unwrap()) + } + } +} + +/// Get the system table while runtime services are active. +pub fn system_table_runtime() -> Option> { + let st = SYSTEM_TABLE.load(Ordering::Acquire); + if st.is_null() { + return None; + } + + // SAFETY: the system table is valid per the requirements of `set_system_table`. + unsafe { + if (*st).runtime_services.is_null() { + None + } else { + Some(SystemTable::::from_ptr(st.cast()).unwrap()) + } + } +} + +/// Common trait implemented by all standard UEFI tables. +pub trait Table { + /// A unique number assigned by the UEFI specification + /// to the standard tables. + const SIGNATURE: u64; +} diff --git a/uefi/src/table/runtime.rs b/uefi/src/table/runtime.rs index 3947869e4..97674bc95 100644 --- a/uefi/src/table/runtime.rs +++ b/uefi/src/table/runtime.rs @@ -4,13 +4,15 @@ use super::Revision; use crate::table::boot::MemoryDescriptor; use crate::{CStr16, Error, Result, Status, StatusExt}; use core::fmt::{self, Debug, Display, Formatter}; -use core::mem::MaybeUninit; +use core::mem::{size_of, MaybeUninit}; use core::ptr; +pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader}; pub use uefi_raw::table::runtime::{ ResetType, TimeCapabilities, VariableAttributes, VariableVendor, }; pub use uefi_raw::time::Daylight; +pub use uefi_raw::PhysicalAddress; #[cfg(feature = "alloc")] use { @@ -290,6 +292,41 @@ impl RuntimeServices { ) -> Status { (self.0.set_virtual_address_map)(map_size, desc_size, desc_version, virtual_map) } + + /// Passes capsules to the firmware. Capsules are most commonly used to update system firmware. + pub fn update_capsule( + &self, + capsule_header_array: &[&CapsuleHeader], + capsule_block_descriptors: &[CapsuleBlockDescriptor], + ) -> Result { + unsafe { + (self.0.update_capsule)( + capsule_header_array.as_ptr().cast(), + capsule_header_array.len(), + capsule_block_descriptors.as_ptr() as PhysicalAddress, + ) + .to_result() + } + } + + /// Tests whether a capsule or capsules can be updated via [`RuntimeServices::update_capsule`]. + /// + /// See [`CapsuleInfo`] for details of the information returned. + pub fn query_capsule_capabilities( + &self, + capsule_header_array: &[&CapsuleHeader], + ) -> Result { + let mut info = CapsuleInfo::default(); + unsafe { + (self.0.query_capsule_capabilities)( + capsule_header_array.as_ptr().cast(), + capsule_header_array.len(), + &mut info.maximum_capsule_size, + &mut info.reset_type, + ) + .to_result_with_val(|| info) + } + } } impl super::Table for RuntimeServices { @@ -334,19 +371,61 @@ pub struct TimeParams { pub daylight: Daylight, } -/// Error returned by [`Time`] methods if the input is outside the valid range. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct TimeError; +/// Error returned by [`Time`] methods. A bool value of `true` means +/// the specified field is outside of its valid range. +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct TimeError { + pub year: bool, + pub month: bool, + pub day: bool, + pub hour: bool, + pub minute: bool, + pub second: bool, + pub nanosecond: bool, + pub timezone: bool, + pub daylight: bool, +} + +#[cfg(feature = "unstable")] +impl core::error::Error for TimeError {} impl Display for TimeError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") + if self.year { + writeln!(f, "year not within `1900..=9999`")?; + } + if self.month { + writeln!(f, "month not within `1..=12")?; + } + if self.day { + writeln!(f, "day not within `1..=31`")?; + } + if self.hour { + writeln!(f, "hour not within `0..=23`")?; + } + if self.minute { + writeln!(f, "minute not within `0..=59`")?; + } + if self.second { + writeln!(f, "second not within `0..=59`")?; + } + if self.nanosecond { + writeln!(f, "nanosecond not within `0..=999_999_999`")?; + } + if self.timezone { + writeln!( + f, + "time_zone not `Time::UNSPECIFIED_TIMEZONE` nor within `-1440..=1440`" + )?; + } + if self.daylight { + writeln!(f, "unknown bits set for daylight")?; + } + Ok(()) } } -#[cfg(feature = "unstable")] -impl core::error::Error for TimeError {} - impl Time { /// Unspecified Timezone/local time. const UNSPECIFIED_TIMEZONE: i16 = uefi_raw::time::Time::UNSPECIFIED_TIMEZONE; @@ -367,11 +446,8 @@ impl Time { daylight: params.daylight, pad2: 0, }); - if time.is_valid() { - Ok(time) - } else { - Err(TimeError) - } + + time.is_valid().map(|_| time) } /// Create an invalid `Time` with all fields set to zero. This can @@ -385,10 +461,39 @@ impl Time { Self(uefi_raw::time::Time::invalid()) } - /// True if all fields are within valid ranges, false otherwise. - #[must_use] - pub fn is_valid(&self) -> bool { - self.0.is_valid() + /// `Ok()` if all fields are within valid ranges, `Err(TimeError)` otherwise. + pub fn is_valid(&self) -> core::result::Result<(), TimeError> { + let mut err = TimeError::default(); + if !(1900..=9999).contains(&self.year()) { + err.year = true; + } + if !(1..=12).contains(&self.month()) { + err.month = true; + } + if !(1..=31).contains(&self.day()) { + err.day = true; + } + if self.hour() > 23 { + err.hour = true; + } + if self.minute() > 59 { + err.minute = true; + } + if self.second() > 59 { + err.second = true; + } + if self.nanosecond() > 999_999_999 { + err.nanosecond = true; + } + if self.time_zone().is_some() && !((-1440..=1440).contains(&self.time_zone().unwrap())) { + err.timezone = true; + } + // All fields are false, i.e., within their valid range. + if err == TimeError::default() { + Ok(()) + } else { + Err(err) + } } /// Query the year. @@ -436,7 +541,7 @@ impl Time { /// Query the time offset in minutes from UTC, or None if using local time. #[must_use] pub const fn time_zone(&self) -> Option { - if self.0.time_zone == 2047 { + if self.0.time_zone == Self::UNSPECIFIED_TIMEZONE { None } else { Some(self.0.time_zone) @@ -477,6 +582,69 @@ impl Display for Time { } } +/// Error returned from failing to convert a byte slice into a [`Time`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TimeByteConversionError { + /// One or more fields of the converted [`Time`] is invalid. + InvalidFields(TimeError), + /// The byte slice is not large enough to hold a [`Time`]. + InvalidSize, +} + +impl Display for TimeByteConversionError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::InvalidFields(error) => write!(f, "{error}"), + Self::InvalidSize => write!( + f, + "the byte slice is not large enough to hold a Time struct" + ), + } + } +} + +impl TryFrom<&[u8]> for Time { + type Error = TimeByteConversionError; + + fn try_from(bytes: &[u8]) -> core::result::Result { + if size_of::