diff --git a/.editorconfig b/.editorconfig index a5bf0354..98ad74b1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,5 +11,5 @@ indent_size = 4 trim_trailing_whitespace = true max_line_length = 80 -[{*.nix,*.yml}] +[{*.md,*.nix,*.toml,*.yml}] indent_size = 2 diff --git a/.envrc b/.envrc index 1d953f4b..3550a30f 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use nix +use flake diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b1f8bfa2..30e0d710 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: cargo directory: "/" schedule: - interval: weekly + interval: monthly open-pull-requests-limit: 10 ignore: - dependency-name: "*" @@ -11,7 +11,7 @@ updates: - package-ecosystem: cargo directory: "/integration-test/bins" schedule: - interval: weekly + interval: monthly open-pull-requests-limit: 10 ignore: - dependency-name: "*" diff --git a/.github/workflows/_build-rust.yml b/.github/workflows/_build-rust.yml index d855a822..47a5ed79 100644 --- a/.github/workflows/_build-rust.yml +++ b/.github/workflows/_build-rust.yml @@ -89,11 +89,12 @@ jobs: - name: Code Formatting if: inputs.do-style-check run: cargo fmt --all -- --check - - name: Code Style and Doc Style + - name: "Code style: clippy" if: inputs.do-style-check - run: | - cargo doc --no-deps --document-private-items --features ${{ inputs.features }} --no-default-features - cargo clippy --all-targets --features ${{ inputs.features }} --no-default-features + run: cargo clippy --all-targets --features ${{ inputs.features }} --no-default-features + - name: "Code style: rustdoc" + if: inputs.do-style-check + run: cargo doc --no-deps --document-private-items --features ${{ inputs.features }} --no-default-features - name: Unit Test run: cargo test --verbose - name: Unit Test with Miri @@ -101,8 +102,7 @@ jobs: run: | rustup component add miri # Run with stack-borrow model - # XXX Temporarily, just for multiboot2 crate. - cargo miri test -p multiboot2 + cargo miri test # Run with tree-borrow model - # XXX Temporarily, just for multiboot2 crate. - MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test -p multiboot2 + # TODO currently doesn't work with the ELF section parsing code + # MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test diff --git a/.github/workflows/integrationtest.yml b/.github/workflows/integrationtest.yml index b0851385..420f3e33 100644 --- a/.github/workflows/integrationtest.yml +++ b/.github/workflows/integrationtest.yml @@ -26,13 +26,7 @@ jobs: steps: - name: Check out uses: actions/checkout@v4 - - 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. - nix_path: nixpkgs=channel:nixos-23.11 - - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: cachix/install-nix-action@v31 - name: Set up cargo cache uses: actions/cache@v4 continue-on-error: false @@ -48,6 +42,6 @@ jobs: key: ${{ runner.os }}-${{ github.job }}-${{ hashFiles('integration-test/**/Cargo.toml', 'integration-test/**/Cargo.lock', 'integration-test/bins/rust-toolchain.toml') }} # Have all the "copying into Nix store" messages in a dedicated step for # better log visibility. - - run: nix-shell --run "echo OK" + - run: nix develop --command bash -c "echo OK" # Now, run the actual test. - - run: nix-shell --run run-integrationtest + - run: nix develop --command bash -c "run-integrationtest" diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 0267453c..40cd7568 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v4 # Executes "typos ." - - uses: crate-ci/typos@v1.24.3 + - uses: crate-ci/typos@v1.34.0 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3c914f55..edc6336e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,7 +24,7 @@ jobs: name: build (msrv) uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.75.0 # MSRV + rust-version: 1.85.0 # MSRV do-style-check: false features: builder @@ -42,7 +42,7 @@ jobs: with: rust-version: nightly do-style-check: false - features: builder,unstable + features: builder ### no-std Build ######################### build_nostd_msrv: @@ -50,7 +50,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.75.0 # MSRV + rust-version: 1.85.0 # MSRV do-style-check: false rust-target: thumbv7em-none-eabihf features: builder @@ -99,7 +99,7 @@ jobs: rust-version: nightly do-style-check: false rust-target: thumbv7em-none-eabihf - features: builder,unstable + features: builder ### Style Checks + Doc ##################### style_msrv: @@ -107,7 +107,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.75.0 # MSRV + rust-version: 1.85.0 # MSRV do-style-check: true do-test: false features: builder @@ -130,7 +130,7 @@ jobs: rust-version: nightly do-style-check: true do-test: false - features: builder,unstable + features: builder miri: name: tests with miri (nightly) @@ -141,4 +141,4 @@ jobs: do-style-check: false do-test: false do-miri: true - features: builder,unstable + features: builder diff --git a/.typos.toml b/.typos.toml index 1a9fcd8b..20927403 100644 --- a/.typos.toml +++ b/.typos.toml @@ -10,5 +10,4 @@ Rela = "Rela" grup = "grup" [default.extend-identifiers] -# FOOBAR = "FOOBAR" - +typ = "typ" diff --git a/Cargo.lock b/Cargo.lock index 357c5f9c..4fdd98c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,114 +1,93 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", - "unicode-xid", -] +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "multiboot2" -version = "0.23.0" +version = "0.24.0" dependencies = [ "bitflags", - "derive_more", "log", "multiboot2-common", "ptr_meta", + "thiserror", "uefi-raw", ] [[package]] name = "multiboot2-common" -version = "0.2.0" +version = "0.3.0" dependencies = [ - "derive_more", "ptr_meta", + "thiserror", ] [[package]] name = "multiboot2-header" -version = "0.6.0" +version = "0.7.0" dependencies = [ - "derive_more", "log", "multiboot2", "multiboot2-common", "ptr_meta", + "thiserror", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "ptr_meta" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -116,24 +95,32 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.77" +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] name = "uefi-raw" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b463030b802e1265a3800fab24df95d3229c202c2e408832a206f05b4d1496ca" +checksum = "7cad96b8baaf1615d3fdd0f03d04a0b487d857c1b51b19dcbfe05e2e3c447b78" dependencies = [ "bitflags", - "ptr_meta", "uguid", ] @@ -145,12 +132,6 @@ checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" [[package]] name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-xid" -version = "0.2.5" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index f361d21b..9c3f7298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,27 @@ [workspace] resolver = "2" members = [ - "multiboot2", - "multiboot2-common", - "multiboot2-header", + "multiboot2", + "multiboot2-common", + "multiboot2-header", ] exclude = [ - "integration-test" + "integration-test" ] +package.rust-version = "1.85.0" +package.edition = "2024" +package.license = "MIT/Apache-2.0" [workspace.dependencies] -bitflags = "2.6.0" -derive_more = { version = "1.0.0", default-features = false, features = ["display"] } +bitflags = "2.9.1" log = { version = "~0.4", default-features = false } -multiboot2 = { version = "0.23.0", default-features = false } -multiboot2-common = { version = "0.2.0", default-features = false } -# Warn: 0.3 has multiple very breaking changes -ptr_meta = { version = "~0.2", default-features = false } +ptr_meta = { version = "~0.3", default-features = false, features = ["derive"] } +thiserror = { version = "2.0.12", default-features = false } + +# Intra-workspace dependencies of upstream crates: always point to the latest +# stable version +multiboot2 = { version = "0.24.0", default-features = false } +multiboot2-common = { version = "0.3.0", default-features = false } # This way, the corresponding crate dependency can be normalley referenced by # version, while still the repository version is used transparently during local diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..cd3f1694 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1748437600, + "narHash": "sha256-hYKMs3ilp09anGO7xzfGs3JqEgUqFMnZ8GMAqI6/k04=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "7282cb574e0607e65224d33be8241eae7cfe0979", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..f685eec5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + description = "multiboot2-rs"; + + inputs = { + # We follow the latest stable release of nixpkgs + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + }; + + outputs = + inputs@{ self, nixpkgs, ... }: + let + # Systems definition for dev shells and exported packages, + # independent of the NixOS configurations and modules defined here. We + # just use "every system" here to not restrict any user. However, it + # likely happens that certain packages don't build for/under certain + # systems. + systems = nixpkgs.lib.systems.flakeExposed; + forAllSystems = + function: nixpkgs.lib.genAttrs systems (system: function nixpkgs.legacyPackages.${system}); + in + { + formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); + devShells = forAllSystems (pkgs: { + default = import ./shell.nix { + inherit pkgs; + }; + }); + }; +} diff --git a/integration-test/bins/Cargo.lock b/integration-test/bins/Cargo.lock index 3f508fec..60d3320f 100644 --- a/integration-test/bins/Cargo.lock +++ b/integration-test/bins/Cargo.lock @@ -1,18 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bit_field" @@ -28,36 +28,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", - "unicode-xid", -] +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elf_rs" @@ -65,7 +44,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "894d710b6b07dae25ce69f9227ec2ffa3a3f71dc7f071acea3e1928ab4aeafdf" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "num-traits", ] @@ -81,9 +60,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -91,9 +70,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "multiboot" @@ -106,33 +85,33 @@ dependencies = [ [[package]] name = "multiboot2" -version = "0.23.0" +version = "0.24.0" dependencies = [ - "bitflags 2.6.0", - "derive_more", + "bitflags 2.9.1", "log", "multiboot2-common", - "ptr_meta", + "ptr_meta 0.3.0", + "thiserror", "uefi-raw", ] [[package]] name = "multiboot2-common" -version = "0.2.0" +version = "0.3.0" dependencies = [ - "derive_more", - "ptr_meta", + "ptr_meta 0.3.0", + "thiserror", ] [[package]] name = "multiboot2-header" -version = "0.6.0" +version = "0.7.0" dependencies = [ - "derive_more", "log", "multiboot2", "multiboot2-common", - "ptr_meta", + "ptr_meta 0.3.0", + "thiserror", ] [[package]] @@ -178,9 +157,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -191,7 +170,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" dependencies = [ - "ptr_meta_derive", + "ptr_meta_derive 0.2.0", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive 0.3.0", ] [[package]] @@ -205,6 +193,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "qemu-exit" version = "3.0.2" @@ -213,9 +212,9 @@ checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -257,43 +256,57 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "uefi-raw" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b463030b802e1265a3800fab24df95d3229c202c2e408832a206f05b4d1496ca" dependencies = [ - "bitflags 2.6.0", - "ptr_meta", + "bitflags 2.9.1", + "ptr_meta 0.2.0", "uguid", ] [[package]] name = "uguid" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" +checksum = "0c8352f8c05e47892e7eaf13b34abd76a7f4aeaf817b716e88789381927f199c" [[package]] name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-xid" -version = "0.2.5" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "util" diff --git a/integration-test/bins/Cargo.toml b/integration-test/bins/Cargo.toml index 05bf49d6..af50178b 100644 --- a/integration-test/bins/Cargo.toml +++ b/integration-test/bins/Cargo.toml @@ -14,8 +14,8 @@ lto = true [workspace.dependencies] anyhow = { version = "1.0", default-features = false } log = { version = "0.4", default-features = false } -multiboot2 = { path = "../../multiboot2", features = ["builder", "unstable"] } -multiboot2-header = { path = "../../multiboot2-header", features = ["builder", "unstable"] } +multiboot2 = { path = "../../multiboot2", features = ["builder"] } +multiboot2-header = { path = "../../multiboot2-header", features = ["builder"] } good_memory_allocator = "0.1" util = { path = "./util" } diff --git a/integration-test/bins/multiboot2_chainloader/Cargo.toml b/integration-test/bins/multiboot2_chainloader/Cargo.toml index e3089f53..68bbcb82 100644 --- a/integration-test/bins/multiboot2_chainloader/Cargo.toml +++ b/integration-test/bins/multiboot2_chainloader/Cargo.toml @@ -2,7 +2,7 @@ name = "multiboot2_chainloader" description = "Integrationtest: Multiboot2 chainloader" version = "0.1.0" -edition = "2021" +edition = "2024" publish = false [dependencies] diff --git a/integration-test/bins/multiboot2_chainloader/src/main.rs b/integration-test/bins/multiboot2_chainloader/src/main.rs index 59c2940c..5462c579 100644 --- a/integration-test/bins/multiboot2_chainloader/src/main.rs +++ b/integration-test/bins/multiboot2_chainloader/src/main.rs @@ -1,6 +1,5 @@ #![no_main] #![no_std] -#![feature(error_in_core)] mod loader; mod multiboot; @@ -16,7 +15,7 @@ core::arch::global_asm!(include_str!("start.S"), options(att_syntax)); /// Entry into the Rust code from assembly using the x86 SystemV calling /// convention. -#[no_mangle] +#[unsafe(no_mangle)] fn rust_entry(multiboot_magic: u32, multiboot_hdr: *const u32) -> ! { init_environment(); let x = 0.12 + 0.56; diff --git a/integration-test/bins/multiboot2_chainloader/src/multiboot.rs b/integration-test/bins/multiboot2_chainloader/src/multiboot.rs index 0a0bd5f6..38b2e934 100644 --- a/integration-test/bins/multiboot2_chainloader/src/multiboot.rs +++ b/integration-test/bins/multiboot2_chainloader/src/multiboot.rs @@ -25,7 +25,7 @@ struct Mem; impl MemoryManagement for Mem { unsafe fn paddr_to_slice(&self, addr: PAddr, size: usize) -> Option<&'static [u8]> { let ptr = addr as *const u64 as *const u8; - Some(slice::from_raw_parts(ptr, size)) + Some(unsafe { slice::from_raw_parts(ptr, size) }) } // If you only want to read fields, you can simply return `None`. diff --git a/integration-test/bins/multiboot2_payload/Cargo.toml b/integration-test/bins/multiboot2_payload/Cargo.toml index 46946abf..d0c3a2e7 100644 --- a/integration-test/bins/multiboot2_payload/Cargo.toml +++ b/integration-test/bins/multiboot2_payload/Cargo.toml @@ -2,7 +2,7 @@ name = "multiboot2_payload" description = "Integrationtest: Multiboot2 payload" version = "0.1.0" -edition = "2021" +edition = "2024" publish = false [dependencies] diff --git a/integration-test/bins/multiboot2_payload/src/main.rs b/integration-test/bins/multiboot2_payload/src/main.rs index 4599bc35..203354a5 100644 --- a/integration-test/bins/multiboot2_payload/src/main.rs +++ b/integration-test/bins/multiboot2_payload/src/main.rs @@ -1,6 +1,5 @@ #![no_main] #![no_std] -#![feature(error_in_core)] extern crate alloc; @@ -16,7 +15,7 @@ use util::{init_environment, qemu_exit_success}; mod verify; /// Entry into the Rust code from assembly. -#[no_mangle] +#[unsafe(no_mangle)] fn rust_entry(multiboot2_magic: u32, multiboot2_hdr: u32) -> ! { main(multiboot2_magic, multiboot2_hdr).expect("Should run multiboot2 integration test"); log::info!("Integration test finished successfully"); diff --git a/integration-test/bins/multiboot2_payload/src/verify/mod.rs b/integration-test/bins/multiboot2_payload/src/verify/mod.rs index aa196487..517c0a97 100644 --- a/integration-test/bins/multiboot2_payload/src/verify/mod.rs +++ b/integration-test/bins/multiboot2_payload/src/verify/mod.rs @@ -48,8 +48,9 @@ pub(self) fn print_memory_map(mbi: &BootInformation) -> anyhow::Result<()> { pub(self) fn print_elf_info(mbi: &BootInformation) -> anyhow::Result<()> { let sections_iter = mbi - .elf_sections() + .elf_sections_tag() .ok_or("Should have elf sections") + .map(|tag| tag.sections()) .map_err(anyhow::Error::msg)?; println!("ELF sections:"); for s in sections_iter { diff --git a/integration-test/bins/rust-toolchain.toml b/integration-test/bins/rust-toolchain.toml index 4349337f..a89ae50a 100644 --- a/integration-test/bins/rust-toolchain.toml +++ b/integration-test/bins/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-04-30" # rustc 1.80-nightly +channel = "nightly-2025-05-31" # rustc 1.89-nightly profile = "default" components = [ "rust-src", diff --git a/multiboot2-common/CHANGELOG.md b/multiboot2-common/CHANGELOG.md index bc3fd61b..5ee4c12f 100644 --- a/multiboot2-common/CHANGELOG.md +++ b/multiboot2-common/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog for Crate `multiboot2-common` -## Unreleased +## v0.3.0 (2025-06-01) + +- **Breaking:** Removed the optional `unstable` feature (required nightly) + - `core::error::Error` is now implemented unconditionally +- **Breaking:** The MSRV is now 1.85 + +## v0.2.1 (2024-09-19) + +- Documentation improvements ## v0.2.0 (2024-09-17) diff --git a/multiboot2-common/Cargo.toml b/multiboot2-common/Cargo.toml index 73d040cc..103351e6 100644 --- a/multiboot2-common/Cargo.toml +++ b/multiboot2-common/Cargo.toml @@ -3,35 +3,34 @@ name = "multiboot2-common" description = """ Common helpers for the `multiboot2` and `multiboot2-header` crates. """ -version = "0.2.0" +version = "0.3.0" authors = [ - "Philipp Schuster " + "Philipp Schuster " ] -license = "MIT/Apache-2.0" -edition = "2021" categories = [ - "no-std", - "no-std::no-alloc", + "no-std", + "no-std::no-alloc", ] keywords = [ - "Multiboot2" + "Multiboot2" ] readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2/tree/main/multiboot2-common" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2-common" -rust-version = "1.75" +edition.workspace = true +license.workspace = true +rust-version.workspace = true [features] default = ["builder"] alloc = [] builder = ["alloc"] -unstable = [] [dependencies] -derive_more.workspace = true ptr_meta.workspace = true +thiserror.workspace = true [package.metadata.docs.rs] all-features = true diff --git a/multiboot2-common/README.md b/multiboot2-common/README.md index 4694864f..b6dbe506 100644 --- a/multiboot2-common/README.md +++ b/multiboot2-common/README.md @@ -5,26 +5,48 @@ Common helpers for the `multiboot2` and `multiboot2-header` crates. -## Architecture +## Architecture Diagrams The following figures, not displayable in `lib.rs` / on `docs.rs` unfortunately, -outline the design of this crate: +outline the design of this crate. In the following figure, you can see the +four classes of Multiboot2 structures and their memory properties. The four +kinds of Multiboot2 structures are boot information, boot information +tags, header, and header tags. All share the same technical foundation: They +have a common header and a possible dynamic size, depending on the header. -![Overview Multiboot2 Structures](./overview-multiboot2-structures.drawio.png "Overview Multiboot2 Structures") +![Overview Multiboot2 structures](./overview-multiboot2-structures.drawio.png "Overview of Multiboot2 structures and their memory properties") -Overview of Multiboot2 structures: Multiboot2 boot information, boot -information tags, Multiboot2 headers, and Multiboot2 header tags all share the -same technical foundation: They have a common header and a possible dynamic -size, depending on the header. +In the next figure, you see how the types from `multiboot2-common` are used +to parse a raw byte slice as the corresponding Multiboot2 structure a safe +manner. The `BytesRef` wrapper ensures basic memory guarantees for the +underlying `&[u8]` slice, while `DynSizedStructure` can then be used to +safely cast to the target type. -![Crate Architecture Overview](./architecture.drawio.png "Crate Architecture Overview") +![Generic parsing flow overview](./parsing-flow-generic.drawio.png "Generic parsing flow overview: From raw bytes to specific structures") -Overview of how raw bytes are modelled to be representable by high-level -ABI-compatible rusty types. +The next figure is like the previous figure, but shows a more specific parsing +flow by using example types of the `multiboot2` crate. Specifically, it shows +how the header structs for each multiboot2 structure, each implementing +the `Header` trait, are utilized as generic types to get the right size +information of the final type tag type. + +Green shows the raw memory, purple boxes refer to logic in `multiboot2-common`, +and red components show structs from the `multiboot2` crate. + +![Specific parsing flow overview](./parsing-flow-specific.drawio.png "Specific parsing flow overview: From raw bytes to multiboot2 structures") + +The last complex figure shows all traits and structs from `multiboot2-common`, +their internal relation, and how consumers (`multiboot2` and +`multiboot2-header`) consume them. As this figure is quite complex, we recommend +to first study the inner box (`multiboot2-common`) and then study how types from +`multiboot2` (orange) and `multiboot2-header` (green) interface with +`multiboot2-common`. + +![Architecture overview](./architecture.drawio.png "Architecture overview") ## MSRV -The MSRV is 1.75.0 stable. +The MSRV is 1.85.0 stable. ## License & Contribution diff --git a/multiboot2-common/architecture.drawio.png b/multiboot2-common/architecture.drawio.png index de80e91e..38fffab1 100644 Binary files a/multiboot2-common/architecture.drawio.png and b/multiboot2-common/architecture.drawio.png differ diff --git a/multiboot2-common/architecture.drawio.xml b/multiboot2-common/architecture.drawio.xml index ab1dc7eb..7fc2f26c 100644 --- a/multiboot2-common/architecture.drawio.xml +++ b/multiboot2-common/architecture.drawio.xml @@ -1,217 +1,507 @@ - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + + + + + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + - - + + - - - + + + - + - - + + + + + + - - - + + + + + + + + - - + + + + + + + + + - - + + - + - - + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + - - + + + + + + - - - + + + - + + + + - - + + - - - + + - + + + + + + + + + + + + + + + + + - - + + - - - + + - - - - + + + - - + + - + - - + + + + + + - - + + - + + + + + + - - + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + - - + + + + + + + + + - - + + + + - - + + + + + + + + + - - + + + + - - + + + + + + + + - - + + + + diff --git a/multiboot2-common/overview-multiboot2-structures.drawio.png b/multiboot2-common/overview-multiboot2-structures.drawio.png index f8971427..2b1e10eb 100644 Binary files a/multiboot2-common/overview-multiboot2-structures.drawio.png and b/multiboot2-common/overview-multiboot2-structures.drawio.png differ diff --git a/multiboot2-common/overview-multiboot2-structures.drawio.xml b/multiboot2-common/overview-multiboot2-structures.drawio.xml index 898f0633..3d0765c4 100644 --- a/multiboot2-common/overview-multiboot2-structures.drawio.xml +++ b/multiboot2-common/overview-multiboot2-structures.drawio.xml @@ -1,95 +1,116 @@ - + - + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - - - + - - + + - - + + - + - + - + - + - + - - + + - - + + - + - - + + + + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/multiboot2-common/parsing-flow-generic.drawio.png b/multiboot2-common/parsing-flow-generic.drawio.png new file mode 100644 index 00000000..a3d0e18c Binary files /dev/null and b/multiboot2-common/parsing-flow-generic.drawio.png differ diff --git a/multiboot2-common/parsing-flow-generic.drawio.xml b/multiboot2-common/parsing-flow-generic.drawio.xml new file mode 100644 index 00000000..1e87a57d --- /dev/null +++ b/multiboot2-common/parsing-flow-generic.drawio.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/multiboot2-common/parsing-flow-specific.drawio.png b/multiboot2-common/parsing-flow-specific.drawio.png new file mode 100644 index 00000000..072bfc90 Binary files /dev/null and b/multiboot2-common/parsing-flow-specific.drawio.png differ diff --git a/multiboot2-common/parsing-flow-specific.drawio.xml b/multiboot2-common/parsing-flow-specific.drawio.xml new file mode 100644 index 00000000..5448e300 --- /dev/null +++ b/multiboot2-common/parsing-flow-specific.drawio.xml @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/multiboot2-common/src/boxed.rs b/multiboot2-common/src/boxed.rs index 24a8906d..6e79ba67 100644 --- a/multiboot2-common/src/boxed.rs +++ b/multiboot2-common/src/boxed.rs @@ -1,6 +1,6 @@ //! Module for [`new_boxed`]. -use crate::{increase_to_alignment, Header, MaybeDynSized, ALIGNMENT}; +use crate::{ALIGNMENT, Header, MaybeDynSized, increase_to_alignment}; use alloc::boxed::Box; use core::alloc::Layout; use core::mem; @@ -88,8 +88,8 @@ pub fn clone_dyn + ?Sized>(tag: &T) -> Box #[cfg(test)] mod tests { use super::*; - use crate::test_utils::{DummyDstTag, DummyTestHeader}; use crate::Tag; + use crate::test_utils::{DummyDstTag, DummyTestHeader}; #[test] fn test_new_boxed() { diff --git a/multiboot2-common/src/bytes_ref.rs b/multiboot2-common/src/bytes_ref.rs index f2291408..13b4e389 100644 --- a/multiboot2-common/src/bytes_ref.rs +++ b/multiboot2-common/src/bytes_ref.rs @@ -1,6 +1,6 @@ //! Module for [`BytesRef`]. -use crate::{Header, MemoryError, ALIGNMENT}; +use crate::{ALIGNMENT, Header, MemoryError}; use core::marker::PhantomData; use core::mem; use core::ops::Deref; diff --git a/multiboot2-common/src/iter.rs b/multiboot2-common/src/iter.rs index 3c1a1dfb..75e5c78c 100644 --- a/multiboot2-common/src/iter.rs +++ b/multiboot2-common/src/iter.rs @@ -2,16 +2,19 @@ //! Multiboot2 information tags and iterating Multiboot2 header tags is the //! same. -use crate::{increase_to_alignment, DynSizedStructure, Header, ALIGNMENT}; +use crate::{ALIGNMENT, DynSizedStructure, Header, increase_to_alignment}; use core::marker::PhantomData; use core::mem; /// Iterates over the tags (modelled by [`DynSizedStructure`]) of the underlying -/// byte slice. Each tag is expected to have the same common [`Header`]. +/// byte slice. Each tag is expected to have the same common [`Header`] with +/// the corresponding ABI guarantees. /// /// As the iterator emits elements of type [`DynSizedStructure`], users should -/// casted them to specific [`Tag`]s using [`DynSizedStructure::cast`] following -/// a user policy. This can for example happen on the basis of some ID. +/// cast them to specific [`Tag`]s using [`DynSizedStructure::cast`] following +/// a user-specific policy. This can for example happen on the basis of some ID. +/// +/// This iterator also emits end tags and doesn't treat them separately. /// /// This type ensures the memory safety guarantees promised by this crates /// documentation. @@ -30,6 +33,8 @@ pub struct TagIter<'a, H: Header> { impl<'a, H: Header> TagIter<'a, H> { /// Creates a new iterator. #[must_use] + // TODO we could take a BytesRef here, but the surrounding code should be + // bullet-proof enough. pub fn new(mem: &'a [u8]) -> Self { // Assert alignment. assert_eq!(mem.as_ptr().align_offset(ALIGNMENT), 0); @@ -81,8 +86,8 @@ impl<'a, H: Header + 'a> Iterator for TagIter<'a, H> { #[cfg(test)] mod tests { - use crate::test_utils::{AlignedBytes, DummyTestHeader}; use crate::TagIter; + use crate::test_utils::{AlignedBytes, DummyTestHeader}; use core::borrow::Borrow; #[test] diff --git a/multiboot2-common/src/lib.rs b/multiboot2-common/src/lib.rs index d4ed7e18..2e38a688 100644 --- a/multiboot2-common/src/lib.rs +++ b/multiboot2-common/src/lib.rs @@ -4,23 +4,57 @@ //! //! The main value-add of this crate is to abstract away the parsing and //! construction of Multiboot2 structures. This is more complex as it may sound -//! at first due to the difficulties listed below. +//! at first due to the difficulties listed below. Further, functionality for +//! the iteration of tags are provided. //! //! The abstractions provided by this crate serve as the base to work with the -//! following structures: +//! following structures in interaction: //! - multiboot2: -//! - boot information structure (whole) +//! - boot information +//! - boot information header (the fixed sized begin portion of a boot +//! information) //! - boot information tags +//! - boot information tag header (the fixed sized begin portion of a tag) //! - multiboot2-header: -//! - header structure (whole) +//! - header +//! - header header (the fixed sized begin portion of a header) //! - header tags +//! - header tag header (the fixed sized begin portion of a tag) //! -//! # Solved Problem & Difficulties Along the Way +//! # TL;DR: Specific Example //! -//! Firstly, the design choice to have ABI-compatible rusty types influenced the -//! requirements and difficulties along the way. They, on the other side, -//! influenced the design. The outcome is what we perceive as the optimal rusty -//! and convenient solution. +//! To name a specific example, the `multiboot2` crate just needs the following +//! types: +//! +//! - `BootInformationHeader` implementing [`Header`] +//! - `BootInformation` wrapping [`DynSizedStructure`] +//! - `type TagIter<'a> = multiboot2_common::TagIter<'a, TagHeader>` +//! ([`TagIter`]) +//! - `TagHeader` implementing [`Header`] +//! - Structs for each tag, each implementing [`MaybeDynSized`] +//! +//! Then, all the magic using the [`TagIter`] and [`DynSizedStructure::cast`] +//! can easily be utilized. +//! +//! The same correspondingly applies to the structures in `multiboot2-header`. +//! +//! # Design, Solved Problem, and Difficulties along the Way +//! +//! Firstly, the design choice to have ABI-compatible rusty types in +//! `multiboot2` and `multiboot2-header` mainly influenced the requirements and +//! difficulties along the way. These obstacles on the other side, influenced +//! the design. The outcome is what we perceive as the optimal rusty and +//! convenient solution. +//! +//! ## Architecture Diagrams +//! +//! The figures in the [README](https://crates.io/crates/multiboot2-common) +//! (currently not embeddable in lib.rs unfortunately) provides an overview of +//! the parsing of Multiboot2 structures and how the definitions from this +//! crate are used. +//! +//! Note that although the diagrams seem complex, most logic is in +//! `multiboot2-common`. For downstream users, the usage is quite simple. //! //! ## Multiboot2 Structures //! @@ -83,15 +117,17 @@ //! //! The overall common abstractions needed to solve the problems mentioned in //! this section are also mainly influenced by the fact that the `multiboot2` -//! and `multiboot2-header` crates use a **zero-copy** design for parsing -//! the corresponding structures. +//! and `multiboot2-header` crates use a **zero-copy** design by parsing +//! the corresponding raw bytes with **ABI-compatible types** owning all their +//! memory. //! -//! Further, by having **ABI-compatible types** that fully represent the -//! reality, we can use the same type for parsing **and** for construction, -//! as modelled in the following simplified example: +//! Further, by having ABI-compatible types that fully represent the reality, we +//! can use the same type for parsing **and** for construction, as modelled in +//! the following simplified example: //! //! ```rust,ignore //! /// ABI-compatible tag for parsing. +//! #[repr(C)] //! pub struct MemoryMapTag { //! header: TagHeader, //! entry_size: u32, @@ -112,16 +148,23 @@ //! //! ## Creating Fat Pointers with [`ptr_meta`] //! +//! Fat pointers are a language feature and the base for references to +//! dynamically sized types, such as `&str`, `&[T]`, `dyn T` or +//! `&DynamicallySizedStruct`. +//! +//! Currently, they can't be created using the standard library, but +//! [`ptr_meta`] can be utilized. +//! //! To create fat pointers with [`ptr_meta`], each tag needs a `Metadata` type //! which is either `usize` (for DSTs) or `()`. A trait is needed to abstract -//! above sized or unsized types. +//! above sized or unsized types. This is done by [`MaybeDynSized`]. //! //! ## Multiboot2 Requirements //! //! All tags must be 8-byte aligned. The actual payload of tags may be followed //! by padding zeroes to fill the gap until the next alignment boundary, if //! necessary. These zeroes are not reflected in the tag's size, but for Rust, -//! must be reflected in the memory allocation size. +//! must be reflected in the type's memory allocation. //! //! ## Rustc Requirements //! @@ -141,15 +184,11 @@ //! [`Layout`] for the underlying type equals the one we manually used for the //! allocation. //! -//! # Architecture & Provided Abstractions -//! -//! Figure 2 in the [README](https://crates.io/crates/multiboot2-common) -//! (currently not embeddable in lib.rs unfortunately) provides an overview of -//! the parsing of Multiboot2 structures and how the definitions from this -//! crate are used. -//! //! ## Parsing and Casting //! +//! The general idea of parsing is that the lifetime of the original byte slice +//! propagates through to references of target types. +//! //! First, we need byte slices which are guaranteed to be aligned and are a //! multiple of the alignment. We have [`BytesRef`] for that. With that, we can //! create a [`DynSizedStructure`]. This is a rusty type that owns all the bytes @@ -181,7 +220,6 @@ //! [`Layout`]: core::alloc::Layout #![no_std] -#![cfg_attr(feature = "unstable", feature(error_in_core))] // --- BEGIN STYLE CHECKS --- #![deny( clippy::all, @@ -222,6 +260,7 @@ use core::mem; use core::ptr; use core::ptr::NonNull; use core::slice; +use thiserror::Error; /// The alignment of all Multiboot2 data structures. pub const ALIGNMENT: usize = 8; @@ -253,25 +292,30 @@ pub trait Header: Clone + Sized + PartialEq + Eq + Debug { } /// An C ABI-compatible dynamically sized type with a common sized [`Header`] -/// and a dynamic amount of bytes. +/// and a dynamic amount of bytes without hidden implicit padding. /// -/// This structures owns all its bytes, unlike [`Header`]. Instances guarantees -/// that the memory requirements promised in the crates description are -/// respected. +/// This structures combines a [`Header`] with the logically owned data by +/// that header according to the reported [`Header::payload_len`]. Instances +/// guarantees that the memory requirements promised in the crates description +/// are respected. /// /// This can be a Multiboot2 header tag, information tag, boot information, or -/// a Multiboot2 header. Depending on the context, the [`Header`] is different. +/// a Multiboot2 header. It is the base for **same-size casts** to these +/// corresponding structures using [`DynSizedStructure::cast`]. Depending on the +/// context, the [`Header`] is different (header header, boot information +/// header, header tag header, or boot information tag header). /// /// # ABI /// This type uses the C ABI. The fixed [`Header`] portion is always there. /// Further, there is a variable amount of payload bytes. Thus, this type can /// only exist on the heap or references to it can be made by cast via fat -/// pointers. +/// pointers. The main constructor is [`DynSizedStructure::ref_from_bytes`]. /// -/// As there might be padding necessary for the proper Rust layout, +/// As terminating padding might be necessary for the proper Rust type layout, /// `size_of_val(&self)` might report additional padding bytes that are not /// reflected by the actual payload. These additional padding bytes however -/// will be reflected in corresponding [`BytesRef`] instances. +/// will be reflected in corresponding [`BytesRef`] instances from that this +/// structure was created. #[derive(Debug, PartialEq, Eq, ptr_meta::Pointee)] #[repr(C, align(8))] pub struct DynSizedStructure { @@ -360,6 +404,8 @@ impl DynSizedStructure { /// /// [`size_of_val`]: mem::size_of_val pub fn cast + ?Sized>(&self) -> &T { + // Thin or fat pointer, depending on type. + // However, only thin ptr is needed. let base_ptr = ptr::addr_of!(*self); // This should be a compile-time assertion. However, this is the best @@ -367,6 +413,7 @@ impl DynSizedStructure { assert!(T::BASE_SIZE >= mem::size_of::()); let t_dst_size = T::dst_len(self.header()); + // Creates thin or fat pointer, depending on type. let t_ptr = ptr_meta::from_raw_parts(base_ptr.cast(), t_dst_size); let t_ref = unsafe { &*t_ptr }; @@ -377,27 +424,29 @@ impl DynSizedStructure { } /// Errors that may occur when working with memory. -#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, derive_more::Display)] +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Error)] pub enum MemoryError { /// The memory points to null. + #[error("memory points to null")] Null, /// The memory must be at least [`ALIGNMENT`]-aligned. + #[error("memory is not properly aligned")] WrongAlignment, /// The memory must cover at least the length of the sized structure header /// type. + #[error("memory range is shorter than the size of the header structure")] ShorterThanHeader, /// The buffer misses the terminating padding to the next alignment /// boundary. The padding is relevant to satisfy Rustc/Miri, but also the /// spec mandates that the padding is added. + #[error("memory is missing required padding")] MissingPadding, /// The size-property has an illegal value that can't be fulfilled with the /// given bytes. + #[error("the header reports an invalid total size")] InvalidReportedTotalSize, } -#[cfg(feature = "unstable")] -impl core::error::Error for MemoryError {} - /// Increases the given size to the next alignment boundary, if it is not a /// multiple of the alignment yet. /// diff --git a/multiboot2-common/src/tag.rs b/multiboot2-common/src/tag.rs index 1501ede5..8b1876e3 100644 --- a/multiboot2-common/src/tag.rs +++ b/multiboot2-common/src/tag.rs @@ -64,7 +64,7 @@ pub trait MaybeDynSized: Pointee { /// data, read the tag size from [`Self::header`] and create a sub slice. fn as_bytes(&self) -> BytesRef { let ptr = core::ptr::addr_of!(*self); - // Actual tag size, optionally with terminating padding. + // Actual tag size with optional terminating padding. let size = mem::size_of_val(self); let slice = unsafe { slice::from_raw_parts(ptr.cast::(), size) }; // Unwrap is fine as this type can't exist without the underlying memory @@ -90,6 +90,8 @@ pub trait Tag: MaybeDynSized { const ID: Self::IDType; } +// This implementation is not needed for parsing but for creation, when +// downstream types just wrap this type. impl MaybeDynSized for DynSizedStructure { type Header = H; diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index defc36d3..ffa901ad 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog for Crate `multiboot2-header` -## Unreleased +## v0.7.0 (2025-06-01) + +- **Breaking:** Removed the optional `unstable` feature (required nightly) + - `core::error::Error` is now implemented unconditionally +- **Breaking:** The MSRV is now 1.85 ## v0.6.0 (2024-09-17) @@ -17,7 +21,7 @@ This release contains a major refactoring of the internals, guaranteeing even more sanity checks for correct behaviour and lack of UB. In this release, the `Builder` was rewritten and lots of corresponding UB in certain -corer-cases removed. Further, the builder's API was streamlined. +corner cases removed. Further, the builder's API was streamlined. If you are interested in the internals of the major refactorings recently taken place, please head to the documentation of `multiboot2-common`. @@ -29,24 +33,28 @@ place, please head to the documentation of `multiboot2-common`. changed. - Updated to latest `multiboot2` dependency -## 0.4.0 (2024-05-01) +All previous versions have been marked as **YANKED**. `0.5.0` is the first +version where all unit tests are passed by Miri, i.e., the first version +without Undefined Behavior. + +## 0.4.0 (2024-05-01) (**YANKED**) - added `EndHeaderTag::default()` - MSRV is 1.70 - Can add multiple `TagType::Smbios` tags in the builder. -## 0.3.2 (2023-11-30) +## 0.3.2 (2023-11-30) (**YANKED**) - **BREAKING** bumped `multiboot2` dependency to `v0.19.0` - the `multiboot2` dependency doesn't pull in the `multiboot2/builder` feature anymore - doc update -## 0.3.1 (2023-06-28) +## 0.3.1 (2023-06-28) (**YANKED**) - doc update -## 0.3.0 (2023-06-23) +## 0.3.0 (2023-06-23) (**YANKED**) - **BREAKING** MSRV is 1.68.0 (UPDATE: This is actually 1.69.) - **BREAKING** renamed the `std` feature to `alloc` @@ -57,9 +65,9 @@ place, please head to the documentation of `multiboot2-common`. - **BREAKING** `HeaderBuilder::build` now returns a value of type `HeaderBytes` The old builder could produce misaligned structures. - added the optional `unstable` feature (requires nightly) - - implement `core::error::Error` for `LoadError` + - implement `core::error::Error` for `LoadError` -## 0.2.0 (2022-05-03) +## 0.2.0 (2022-05-03) (**YANKED**) - **BREAKING** renamed `EntryHeaderTag` to `EntryAddressHeaderTag` - **BREAKING** some paths changed from `multiboot2_header::header` @@ -67,7 +75,7 @@ place, please head to the documentation of `multiboot2-common`. -> thus, import paths are much more logically now - internal code improvements -## 0.1.1 (2022-05-02) +## 0.1.1 (2022-05-02) (**YANKED**) - fixed a bug that prevented the usage of the crate in `no_std` environments - added a new default `builder`-feature to Cargo which requires the `alloc` @@ -75,7 +83,7 @@ place, please head to the documentation of `multiboot2-common`. (this feature can be disabled which will also remove the dependency to the `alloc` crate) -## 0.1.0 (2021-10-08) +## 0.1.0 (2021-10-08) (**YANKED**) - initial release diff --git a/multiboot2-header/Cargo.toml b/multiboot2-header/Cargo.toml index b5d33ea3..01f05376 100644 --- a/multiboot2-header/Cargo.toml +++ b/multiboot2-header/Cargo.toml @@ -6,22 +6,20 @@ contained header tags. Usable in no_std environments, such as a bootloader. An optional builder feature also allows the construction of the corresponding structures. """ -version = "0.6.0" +version = "0.7.0" authors = [ - "Philipp Schuster " + "Philipp Schuster " ] -license = "MIT/Apache-2.0" -edition = "2021" categories = [ - "no-std", - "no-std::no-alloc", - "parsing", + "no-std", + "no-std::no-alloc", + "parsing", ] keywords = [ - "Multiboot2", - "kernel", - "boot", - "bootloader", + "Multiboot2", + "kernel", + "boot", + "bootloader", ] # without this, sometimes crates.io doesn't show the preview of the README # I expeciended this multiple times in the past @@ -29,7 +27,9 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2/tree/main/multiboot2-header" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2-header" -rust-version = "1.75" +edition.workspace = true +license.workspace = true +rust-version.workspace = true [[example]] name = "minimal" @@ -39,15 +39,13 @@ required-features = ["builder"] default = ["builder"] alloc = ["multiboot2-common/alloc"] builder = ["alloc", "multiboot2-common/builder"] -# Nightly-only features, which will eventually be stabilized. -unstable = ["multiboot2-common/unstable"] [dependencies] -derive_more.workspace = true log.workspace = true multiboot2-common.workspace = true multiboot2.workspace = true ptr_meta.workspace = true +thiserror.workspace = true [package.metadata.docs.rs] all-features = true diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index 170615f6..bb6407ce 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -46,25 +46,25 @@ use multiboot2_header::{HeaderTagFlag, HeaderTagISA, MbiTagType, RelocatableHead /// Small example that creates a Multiboot2 header and parses it afterwards. fn main() { - // We create a Multiboot2 header during runtime here. A practical example is that your - // program gets the header from a file and parses it afterwards. - let mb2_hdr_bytes = Multiboot2HeaderBuilder::new(HeaderTagISA::I386) - .relocatable_tag(RelocatableHeaderTag::new( - HeaderTagFlag::Required, - 0x1337, - 0xdeadbeef, - 4096, - RelocatableHeaderTagPreference::None, - )) - .information_request_tag( - InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required) - .add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]), - ) - .build(); - - // Cast bytes in vector to Multiboot2 information structure - let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr_bytes.as_ptr().cast()) }; - println!("{:#?}", mb2_hdr); + // We create a Multiboot2 header during runtime here. A practical example is that your + // program gets the header from a file and parses it afterwards. + let mb2_hdr_bytes = Multiboot2HeaderBuilder::new(HeaderTagISA::I386) + .relocatable_tag(RelocatableHeaderTag::new( + HeaderTagFlag::Required, + 0x1337, + 0xdeadbeef, + 4096, + RelocatableHeaderTagPreference::None, + )) + .information_request_tag( + InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required) + .add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]), + ) + .build(); + + // Cast bytes in vector to Multiboot2 information structure + let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr_bytes.as_ptr().cast()) }; + println!("{:#?}", mb2_hdr); } ``` @@ -75,7 +75,7 @@ include it like this: ``` #[used] -#[no_mangle] +#[unsafe(no_mangle)] #[link_section = ".text.multiboot2_header"] static MULTIBOOT2_HDR: [u8; 64] = *include_bytes!("mb2_hdr_dump.bin"); ``` @@ -85,7 +85,7 @@ bytes of the ELF. See Multiboot2 specification. ## MSRV -The MSRV is 1.75.0 stable. +The MSRV is 1.85.0 stable. ## License & Contribution diff --git a/multiboot2-header/examples/minimal.rs b/multiboot2-header/examples/minimal.rs index e77c02bc..5dcabd00 100644 --- a/multiboot2-header/examples/minimal.rs +++ b/multiboot2-header/examples/minimal.rs @@ -31,5 +31,5 @@ fn main() { let ptr = mb2_hdr_bytes.as_bytes().as_ptr(); let mb2_hdr = unsafe { Multiboot2Header::load(ptr.cast()) }; let mb2_hdr = mb2_hdr.unwrap(); - println!("{:#?}", mb2_hdr); + println!("{mb2_hdr:#?}"); } diff --git a/multiboot2-header/src/builder.rs b/multiboot2-header/src/builder.rs index 9017154a..1c9598d8 100644 --- a/multiboot2-header/src/builder.rs +++ b/multiboot2-header/src/builder.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloc::boxed::Box; use alloc::vec::Vec; -use multiboot2_common::{new_boxed, DynSizedStructure, MaybeDynSized}; +use multiboot2_common::{DynSizedStructure, MaybeDynSized, new_boxed}; /// Builder for a Multiboot2 header information. #[derive(Debug)] diff --git a/multiboot2-header/src/entry_address.rs b/multiboot2-header/src/entry_address.rs index a4940e7b..852d481f 100644 --- a/multiboot2-header/src/entry_address.rs +++ b/multiboot2-header/src/entry_address.rs @@ -53,7 +53,7 @@ impl Debug for EntryAddressHeaderTag { .field("type", &self.typ()) .field("flags", &self.flags()) .field("size", &self.size()) - .field("entry_addr", &(self.entry_addr as *const u32)) + .field("entry_addr", &self.entry_addr) .finish() } } diff --git a/multiboot2-header/src/entry_efi_32.rs b/multiboot2-header/src/entry_efi_32.rs index 34a8d26f..1a40d559 100644 --- a/multiboot2-header/src/entry_efi_32.rs +++ b/multiboot2-header/src/entry_efi_32.rs @@ -62,7 +62,7 @@ impl Debug for EntryEfi32HeaderTag { .field("type", &self.typ()) .field("flags", &self.flags()) .field("size", &self.size()) - .field("entry_addr", &(self.entry_addr as *const u32)) + .field("entry_addr", &self.entry_addr) .finish() } } diff --git a/multiboot2-header/src/entry_efi_64.rs b/multiboot2-header/src/entry_efi_64.rs index 1a673496..28787254 100644 --- a/multiboot2-header/src/entry_efi_64.rs +++ b/multiboot2-header/src/entry_efi_64.rs @@ -62,7 +62,7 @@ impl Debug for EntryEfi64HeaderTag { .field("type", &self.typ()) .field("flags", &self.flags()) .field("size", &self.size()) - .field("entry_addr", &(self.entry_addr as *const u32)) + .field("entry_addr", &self.entry_addr) .finish() } } diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index 53963e5e..66c127a0 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -4,12 +4,11 @@ use crate::{ HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag, RelocatableHeaderTag, TagIter, }; -#[cfg(feature = "unstable")] -use core::error::Error; use core::fmt::{Debug, Formatter}; use core::mem::size_of; use core::ptr::NonNull; -use multiboot2_common::{DynSizedStructure, Header, MemoryError, Tag, ALIGNMENT}; +use multiboot2_common::{ALIGNMENT, DynSizedStructure, Header, MemoryError, Tag}; +use thiserror::Error; /// Magic value for a [`Multiboot2Header`], as defined by the spec. pub const MAGIC: u32 = 0xe85250d6; @@ -39,7 +38,7 @@ impl<'a> Multiboot2Header<'a> { /// Multiboot2 header pointer. pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result { let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?; - let inner = DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)?; + let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? }; let this = Self(inner); let header = this.0.header(); @@ -226,25 +225,18 @@ impl Debug for Multiboot2Header<'_> { /// Errors that occur when a chunk of memory can't be parsed as /// [`Multiboot2Header`]. -#[derive(Copy, Clone, Debug, derive_more::Display, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)] pub enum LoadError { /// The provided checksum does not match the expected value. + #[error("checksum does not match expected value")] ChecksumMismatch, /// The header does not contain the correct magic number. + #[error("header does not contain expected magic value")] MagicNotFound, /// The provided memory can't be parsed as [`Multiboot2Header`]. /// See [`MemoryError`]. - Memory(MemoryError), -} - -#[cfg(feature = "unstable")] -impl Error for LoadError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Memory(inner) => Some(inner), - _ => None, - } - } + #[error("memory can't be parsed as multiboot2 header")] + Memory(#[source] MemoryError), } /// The "basic" Multiboot2 header. This means only the properties, that are known during diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index d4066dca..e2a6974e 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -23,10 +23,9 @@ //! //! ## MSRV //! -//! The MSRV is 1.75.0 stable. +//! The MSRV is 1.85.0 stable. #![no_std] -#![cfg_attr(feature = "unstable", feature(error_in_core))] // --- BEGIN STYLE CHECKS --- #![deny( clippy::all, diff --git a/multiboot2-header/src/relocatable.rs b/multiboot2-header/src/relocatable.rs index 987030c7..9575f592 100644 --- a/multiboot2-header/src/relocatable.rs +++ b/multiboot2-header/src/relocatable.rs @@ -108,10 +108,10 @@ impl Debug for RelocatableHeaderTag { .field("flags", &self.flags()) .field("size", &self.size()) // trick to print this as hexadecimal pointer - .field("min_addr", &(self.min_addr as *const u32)) - .field("max_addr", &(self.max_addr as *const u32)) - .field("align", &{ self.align }) - .field("preference", &{ self.preference }) + .field("min_addr", &self.min_addr) + .field("max_addr", &self.max_addr) + .field("align", &self.align) + .field("preference", &self.preference) .finish() } } diff --git a/multiboot2/CHANGELOG.md b/multiboot2/CHANGELOG.md index 592a6b8f..d3847106 100644 --- a/multiboot2/CHANGELOG.md +++ b/multiboot2/CHANGELOG.md @@ -1,15 +1,28 @@ # Changelog for Crate `multiboot2` -## Unreleased +## v0.24.0 (2025-06-01) + +- **Breaking:** Removed the optional `unstable` feature (required nightly) + - `core::error::Error` is now implemented unconditionally +- **Breaking:** The MSRV is now 1.85 +- Fixed a bug causing UB in `ElfSection::name()` + +## v0.23.1 (2024-10-21) + +- Fix wrong tag ID when using `BootdevTag::new` +- `BootInformation::elf_sections` is now deprecated and replaced by the newly +- added `BootInformation::elf_sections_tag`. On the returned type, you can call + `.sections()` to iterate the sections +- Fixed the debug output of `BootInformation` ## v0.23.0 (2024-09-17) - dependency updates - **Breaking:** MSRV is now 1.75 - Added missing tags along with getters for on `BootInformation`: - - `ApmTag` - - `BootdevTag` - - `NetworkTag` + - `ApmTag` + - `BootdevTag` + - `NetworkTag` - `BootInformation::tags` iterator is now public - misc metadata fixes @@ -27,7 +40,7 @@ Minor documentation fixes. This release contains another major refactoring of the internals, guaranteeing even more sanity checks for correct behaviour and lack of UB. In this release, the `Builder` was rewritten and lots of corresponding UB in certain -corer-cases removed. Further, the builder's API was streamlined. +corner cases removed. Further, the builder's API was streamlined. If you are interested in the internals of the major refactorings recently taken place, please head to the documentation of `multiboot2-common`. @@ -56,10 +69,14 @@ Sorry for all the UB that silently slept insight many parts of the code base. This is a community project that has grown over the years. But now, the code base is in excellent shape! -## 0.21.0 (2024-08-17) +All previous versions have been marked as **YANKED**. `0.22.0` is the first +version where all unit tests are passed by Miri, i.e., the first version +without Undefined Behavior. + +## 0.21.0 (2024-08-17) (**YANKED**) -This release contains a massive refactoring of various internals. Now, **almost -**unit tests pass Miri**, thus we removed lots of undefined behaviour and +This release contains a massive refactoring of various internals. Now, almost +all **unit tests pass Miri**, thus we removed lots of undefined behaviour and increased the memory safety! 🎉 Only a small part of these internal refactorings leak to the public interface. If you don't use external custom tags, you should be fine from any refactorings. @@ -87,11 +104,11 @@ release and you'll be fine!** - documentation enhancements - updated dependencies -## 0.20.2 (2024-05-26) +## 0.20.2 (2024-05-26) (**YANKED**) - fix Debug implementation of `EfiMemoryMapTag` -## 0.20.1 (2024-05-26) +## 0.20.1 (2024-05-26) (**YANKED**) - fixed the handling of `EFIMemoryMapTag` and `EFIMemoryAreaIter` - **BREAKING** Fixed wrong creation of `EFIMemoryMapTag`. @@ -99,12 +116,12 @@ release and you'll be fine!** `EFIMemoryMapTag::new_from_map`. - `ModuleTag::new`'s `end` parameter now must be bigger than `start`. -## 0.20.0 (2024-05-01) +## 0.20.0 (2024-05-01) (**YANKED**) - added `InformationBuilder::default()` - MSRV is 1.70 -## 0.19.0 (2023-09-21) +## 0.19.0 (2023-09-21) (**YANKED**) - **BREAKING** MSRV is 1.69.0 - **BREAKING** `Tag::get_dst_str_slice` renamed to @@ -118,11 +135,11 @@ release and you'll be fine!** - `InformationBuilder` now also allows to add custom tags. The new public method `add_tag` was introduced for that. -## 0.18.1 (2023-07-13) +## 0.18.1 (2023-07-13) (**YANKED**) - Documentation improvements -## 0.18.0 (2023-07-13) +## 0.18.0 (2023-07-13) (**YANKED**) - **BREAKING** The `TagTrait` was enhanced and now has an associated `ID` constant. This is only breaking to users that used `BootInformation::get_tag` @@ -143,14 +160,14 @@ release and you'll be fine!** - Better debug output of `BootInformation` and `MemoryArea` - Internal code cleanup. -## 0.17.0 (2023-07-12) +## 0.17.0 (2023-07-12) (**YANKED**) - **BREAKING** Make functions of `InformationBuilder` chainable. They now consume the builder. - **BREAKING** Allow non-standard memory area types by using new pair of corresponding types: `MemoryAreaTypeId` and `MemoryAreaType`. -## 0.16.0 (2023-06-23) +## 0.16.0 (2023-06-23) (**YANKED**) - **BREAKING** renamed `MULTIBOOT2_BOOTLOADER_MAGIC` to `MAGIC` - **BREAKING** `EFIMemoryDesc` was removed and is now an alias of @@ -188,7 +205,7 @@ release and you'll be fine!** - added `BootInformation::load` as new default constructor - added `MemoryMapTag::entry_size` and `MemoryMapTag::entry_version` -## 0.15.1 (2023-03-18) +## 0.15.1 (2023-03-18) (**YANKED**) - **BREAKING** `MemoryMapTag::all_memory_areas()` was renamed to `memory_areas` and now returns `MemoryAreaIter` instead of @@ -203,17 +220,17 @@ release and you'll be fine!** value. This prevents possible panics. - fix: prevent a possible panic in `ElfSection::section_type()` -## 0.15.0 (2023-03-17) +## 0.15.0 (2023-03-17) (**YANKED**) - **BREAKING** MSRV is 1.56.1 - **BREAKING** fixed lifetime issues: `VBEInfoTag` is no longer `&static` - **BREAKING:** `TagType` is now split into `TagTypeId` and `TagType` - - `TagTypeId` is a binary-compatible form of a Multiboot2 tag id - - `TagType` is a higher-level abstraction for either specified or custom - tags - but not ABI compatible. - - There exists a seamless integration between `u32`, `TagType`, and - `TagTypeId` via `From` and `PartialEq`-implementations. + - `TagTypeId` is a binary-compatible form of a Multiboot2 tag id + - `TagType` is a higher-level abstraction for either specified or custom + tags + but not ABI compatible. + - There exists a seamless integration between `u32`, `TagType`, and + `TagTypeId` via `From` and `PartialEq`-implementations. - fixed another internal lifetime issue - `BootInformation::framebuffer_tag()` now returns `Option>` instead of @@ -225,7 +242,7 @@ release and you'll be fine!** (check docs.rs). There is also a small unit test that you can use to learn from. -## 0.14.2 (2023-03-17) +## 0.14.2 (2023-03-17) (**YANKED**) - documentation fixes - `MbiLoadError` now implements `Display` @@ -233,74 +250,74 @@ release and you'll be fine!** With this feature, `MbiLoadError` now implements `core::error::Error` and can be used with `anyhow::Result` for example. -## 0.14.1 (2023-03-09) +## 0.14.1 (2023-03-09) (**YANKED**) - fixed the calculation of the last area of the memory map tag ([#119](https://github.com/rust-osdev/multiboot2/pull/119)) (Previously, iterating the EFI Memory map resulted in a superfluous entry as it ran over the next tag) -## 0.14.0 (2022-06-30) +## 0.14.0 (2022-06-30) (**YANKED**) - **BREAKING CHANGES** \ This version includes a few small breaking changes that brings more safety when parsing strings from the multiboot information structure. - - `BootLoaderNameTag::name` now returns a Result instead of just the value - - `CommandLineTag::command_line` now returns a Result instead of just the - value - - `ModuleTag::cmdline` now returns a Result instead of just the value - - `RsdpV1Tag::signature` now returns a Result instead of an Option - - `RsdpV1Tag::oem_id` now returns a Result instead of an Option - - `RsdpV2Tag::signature` now returns a Result instead of an Option - - `RsdpV2Tag::oem_id` now returns a Result instead of an Option + - `BootLoaderNameTag::name` now returns a Result instead of just the value + - `CommandLineTag::command_line` now returns a Result instead of just the + value + - `ModuleTag::cmdline` now returns a Result instead of just the value + - `RsdpV1Tag::signature` now returns a Result instead of an Option + - `RsdpV1Tag::oem_id` now returns a Result instead of an Option + - `RsdpV2Tag::signature` now returns a Result instead of an Option + - `RsdpV2Tag::oem_id` now returns a Result instead of an Option - internal code improvements -## 0.13.3 (2022-06-03) +## 0.13.3 (2022-06-03) (**YANKED**) - impl `Send` for `BootInformation` -## 0.13.2 (2022-05-02) +## 0.13.2 (2022-05-02) (**YANKED**) - `TagType` now implements `Ord` so that it can be used in `BTreeSet` - small internal improvements and restructuring of the code (no breaking changes to public API) -## 0.13.1 (2022-01-09) +## 0.13.1 (2022-01-09) (**YANKED**) - minor fix -## 0.13.0 (**yanked**) +## 0.13.0 (2022-01-09) (**YANKED**) - added missing getters for tag `ImageLoadPhysAddr` - added missing getters for tags `EFIImageHandle32` and `EFIImageHandle64` -## 0.12.2 (2021-10-02) +## 0.12.2 (2021-10-02) (**YANKED**) - `TagType` now implements `Eq` and `Hash` - internal improvements - - `std` can be used in tests; the crate is still `no_std` - - this implies that `cargo test` doesn't work on "non-standard" targets - - CI (Ubuntu) still works. - - code formatting/style - - sensible style checks as optional CI job - - `.editorconfig` file - - prepared co-existence of crates `multiboot2` and `multiboot2-header` - in a Cargo workspace inside the same repository + - `std` can be used in tests; the crate is still `no_std` + - this implies that `cargo test` doesn't work on "non-standard" targets + - CI (Ubuntu) still works. + - code formatting/style + - sensible style checks as optional CI job + - `.editorconfig` file + - prepared co-existence of crates `multiboot2` and `multiboot2-header` + in a Cargo workspace inside the same repository -## 0.12.1 (2021-08-11) +## 0.12.1 (2021-08-11) (**YANKED**) - `TagType`-enum introduced in `v0.11` is now actually public - internal code improvements -## 0.12.0 (2021-08-06) +## 0.12.0 (2021-08-06) (**YANKED**) - **breaking:** `load()` and `load_with_offset` now returns a result - added public constant `MULTIBOOT2_BOOTLOADER_MAGIC` - Rust edition 2018 (instead of 2015) - internal code improvements -## 0.11.0 (2021-07-07) +## 0.11.0 (2021-07-07) (**YANKED**) - **breaking:** iterator functions (e.g. `ElfSectionsTag::sections()`) return `impl Iterator` instead of a concrete type @@ -309,7 +326,7 @@ release and you'll be fine!** - much improved debug-formatting of `BootInformation` - internal code improvements / formatting -## 0.10.0 (2020-11-03) +## 0.10.0 (2020-11-03) (**YANKED**) - allow access to all memory regions (MemoryMap-Tag) - internal code improvements diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index 3ce6ef71..a79f3386 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -6,25 +6,23 @@ structures and the contained information tags. Usable in `no_std` environments, such as a kernel. An optional builder feature also allows the construction of the corresponding structures. """ -version = "0.23.0" +version = "0.24.0" authors = [ - "Philipp Oppermann ", - "Calvin Lee ", - "Isaac Woods", - "Philipp Schuster " + "Philipp Oppermann ", + "Calvin Lee ", + "Isaac Woods", + "Philipp Schuster " ] -license = "MIT/Apache-2.0" -edition = "2021" categories = [ - "no-std", - "no-std::no-alloc", - "parsing", + "no-std", + "no-std::no-alloc", + "parsing", ] keywords = [ - "Multiboot2", - "kernel", - "boot", - "bootloader", + "Multiboot2", + "kernel", + "boot", + "bootloader", ] # without this, sometimes crates.io doesn't show the preview of the README # I expeciended this multiple times in the past @@ -32,22 +30,22 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2/tree/main/multiboot2" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2" -rust-version = "1.75" +edition.workspace = true +license.workspace = true +rust-version.workspace = true [features] default = ["builder"] alloc = ["multiboot2-common/alloc"] builder = ["alloc", "multiboot2-common/builder"] -# Nightly-only features, which will eventually be stabilized. -unstable = ["multiboot2-common/unstable"] [dependencies] bitflags.workspace = true -derive_more.workspace = true log.workspace = true -ptr_meta.workspace = true multiboot2-common.workspace = true -uefi-raw = { version = "~0.8.0", default-features = false } +ptr_meta.workspace = true +thiserror.workspace = true +uefi-raw = { version = "~0.11.0", default-features = false } [package.metadata.docs.rs] all-features = true diff --git a/multiboot2/README.md b/multiboot2/README.md index efd4694f..de49d1af 100644 --- a/multiboot2/README.md +++ b/multiboot2/README.md @@ -47,7 +47,7 @@ There are many different types of tags, but they all have the same beginning: ## MSRV -The MSRV is 1.75.0 stable. +The MSRV is 1.85.0 stable. ## License & Contribution diff --git a/multiboot2/src/apm.rs b/multiboot2/src/apm.rs index 11211a36..5184f1b1 100644 --- a/multiboot2/src/apm.rs +++ b/multiboot2/src/apm.rs @@ -36,7 +36,7 @@ impl ApmTag { dseg_len: u16, ) -> Self { Self { - header: TagHeader::new(TagType::Apm, mem::size_of::() as u32), + header: TagHeader::new(Self::ID, mem::size_of::() as u32), version, cseg, offset, diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 0bf40116..ba4f0fb5 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -3,41 +3,31 @@ use crate::framebuffer::UnknownFramebufferType; use crate::tag::TagHeader; use crate::{ - module, ApmTag, BasicMemoryInfoTag, BootLoaderNameTag, BootdevTag, CommandLineTag, + ApmTag, BasicMemoryInfoTag, BootLoaderNameTag, BootdevTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag, ModuleIter, NetworkTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, - TagIter, TagType, VBEInfoTag, + TagIter, TagType, VBEInfoTag, module, }; -#[cfg(feature = "unstable")] -use core::error::Error; use core::fmt; use core::mem; use core::ptr::NonNull; -use derive_more::Display; use multiboot2_common::{DynSizedStructure, Header, MaybeDynSized, MemoryError, Tag}; +use thiserror::Error; /// Errors that occur when a chunk of memory can't be parsed as /// [`BootInformation`]. -#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)] pub enum LoadError { /// The provided memory can't be parsed as [`BootInformation`]. /// See [`MemoryError`]. - Memory(MemoryError), + #[error("memory can't be parsed as boot information")] + Memory(#[source] MemoryError), /// Missing mandatory end tag. + #[error("missing mandatory end tag")] NoEndTag, } -#[cfg(feature = "unstable")] -impl Error for LoadError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Memory(inner) => Some(inner), - Self::NoEndTag => None, - } - } -} - /// The basic header of a [`BootInformation`] as sized Rust type. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C, align(8))] @@ -104,7 +94,7 @@ impl<'a> BootInformation<'a> { /// program may observe unsynchronized mutation. pub unsafe fn load(ptr: *const BootInformationHeader) -> Result { let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?; - let inner = DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)?; + let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? }; let this = Self(inner); if !this.has_valid_end_tag() { @@ -132,6 +122,7 @@ impl<'a> BootInformation<'a> { /// Get the start address of the boot info. #[must_use] + // TODO deprecated and use pointers only (see provenance discussions) pub fn start_address(&self) -> usize { self.as_ptr() as usize } @@ -153,6 +144,7 @@ impl<'a> BootInformation<'a> { /// let end_addr = boot_info.start_address() + boot_info.total_size(); /// ``` #[must_use] + // TODO deprecated and use pointers only (see provenance discussions) pub fn end_address(&self) -> usize { self.start_address() + self.total_size() } @@ -252,7 +244,7 @@ impl<'a> BootInformation<'a> { /// # use multiboot2::{BootInformation, BootInformationHeader}; /// # let ptr = 0xdeadbeef as *const BootInformationHeader; /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() }; - /// if let Some(sections) = boot_info.elf_sections() { + /// if let Some(sections) = boot_info.elf_sections_tag().map(|tag| tag.sections()) { /// let mut total = 0; /// for section in sections { /// println!("Section: {:?}", section); @@ -261,14 +253,21 @@ impl<'a> BootInformation<'a> { /// } /// ``` #[must_use] + #[deprecated = "Use elf_sections_tag() instead and corresponding getters"] pub fn elf_sections(&self) -> Option { let tag = self.get_tag::(); tag.map(|t| { assert!((t.entry_size() * t.shndx()) <= t.header().size); - t.sections_iter() + t.sections() }) } + /// Search for the [`ElfSectionsTag`]. + #[must_use] + pub fn elf_sections_tag(&self) -> Option<&ElfSectionsTag> { + self.get_tag() + } + /// Search for the [`FramebufferTag`]. The result is `Some(Err(e))`, if the /// framebuffer type is unknown, while the framebuffer tag is present. #[must_use] @@ -419,50 +418,42 @@ impl<'a> BootInformation<'a> { impl fmt::Debug for BootInformation<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - /// Limit how many Elf-Sections should be debug-formatted. - /// Can be thousands of sections for a Rust binary => this is useless output. - /// If the user really wants this, they should debug-format the field directly. - const ELF_SECTIONS_LIMIT: usize = 7; - let mut debug = f.debug_struct("Multiboot2BootInformation"); debug .field("start_address", &self.start_address()) .field("end_address", &self.end_address()) .field("total_size", &self.total_size()) // now tags in alphabetical order + .field("apm", &self.apm_tag()) .field("basic_memory_info", &(self.basic_memory_info_tag())) .field("boot_loader_name", &self.boot_loader_name_tag()) - // .field("bootdev", &self.bootdev_tag()) + .field("bootdev", &self.bootdev_tag()) .field("command_line", &self.command_line_tag()) .field("efi_bs_not_exited", &self.efi_bs_not_exited_tag()) + .field("efi_ih32", &self.efi_ih32_tag()) + .field("efi_ih64", &self.efi_ih64_tag()) .field("efi_memory_map", &self.efi_memory_map_tag()) .field("efi_sdt32", &self.efi_sdt32_tag()) .field("efi_sdt64", &self.efi_sdt64_tag()) - .field("efi_ih32", &self.efi_ih32_tag()) - .field("efi_ih64", &self.efi_ih64_tag()); - - // usually this is REALLY big (thousands of tags) => skip it here - { - let elf_sections_tag_entries_count = - self.elf_sections().map(|x| x.count()).unwrap_or(0); - - if elf_sections_tag_entries_count > ELF_SECTIONS_LIMIT { - debug.field("elf_sections (count)", &elf_sections_tag_entries_count); - } else { - debug.field("elf_sections", &self.elf_sections()); - } - } - - debug + .field("elf_sections", &self.elf_sections_tag()) .field("framebuffer", &self.framebuffer_tag()) .field("load_base_addr", &self.load_base_addr_tag()) .field("memory_map", &self.memory_map_tag()) .field("modules", &self.module_tags()) - // .field("network", &self.network_tag()) + .field("network", &self.network_tag()) .field("rsdp_v1", &self.rsdp_v1_tag()) .field("rsdp_v2", &self.rsdp_v2_tag()) - .field("smbios_tag", &self.smbios_tag()) - .field("vbe_info_tag", &self.vbe_info_tag()) + .field("smbios", &self.smbios_tag()) + .field("vbe_info", &self.vbe_info_tag()) + // computed fields + .field("custom_tags_count", &{ + self.tags() + .filter(|tag| { + let id: TagType = tag.header().typ.into(); + matches!(id, TagType::Custom(_)) + }) + .count() + }) .finish() } } diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 1b4e45dd..636bd97d 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,7 +1,7 @@ //! Module for [`BootLoaderNameTag`]. use crate::tag::TagHeader; -use crate::{parse_slice_as_string, StringError, TagType}; +use crate::{StringError, TagType, parse_slice_as_string}; use core::fmt::{Debug, Formatter}; use core::mem; use multiboot2_common::{MaybeDynSized, Tag}; diff --git a/multiboot2/src/bootdev.rs b/multiboot2/src/bootdev.rs index 14640e16..6ed43bfc 100644 --- a/multiboot2/src/bootdev.rs +++ b/multiboot2/src/bootdev.rs @@ -19,7 +19,7 @@ impl BootdevTag { #[must_use] pub fn new(biosdev: u32, slice: u32, part: u32) -> Self { Self { - header: TagHeader::new(TagType::Apm, mem::size_of::() as u32), + header: TagHeader::new(Self::ID, mem::size_of::() as u32), biosdev, slice, part, diff --git a/multiboot2/src/builder.rs b/multiboot2/src/builder.rs index 0ddab5de..834b57a5 100644 --- a/multiboot2/src/builder.rs +++ b/multiboot2/src/builder.rs @@ -11,7 +11,7 @@ use crate::{ }; use alloc::boxed::Box; use alloc::vec::Vec; -use multiboot2_common::{new_boxed, DynSizedStructure, MaybeDynSized}; +use multiboot2_common::{DynSizedStructure, MaybeDynSized, new_boxed}; /// Builder for a Multiboot2 header information. // #[derive(Debug)] @@ -378,5 +378,6 @@ mod tests { // Mainly a test for Miri. dbg!(tag.header(), tag.payload().len()); } + eprintln!("{info:#x?}") } } diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index d3cd4b4e..405cfb25 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -1,7 +1,7 @@ //! Module for [`CommandLineTag`]. use crate::tag::TagHeader; -use crate::{parse_slice_as_string, StringError, TagType}; +use crate::{StringError, TagType, parse_slice_as_string}; use core::fmt::{Debug, Formatter}; use core::mem; use core::str; diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index a7f2aad7..a3a14093 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -6,8 +6,8 @@ //! - [`EFIImageHandle64Tag`] //! - [`EFIBootServicesNotExitedTag`] -use crate::tag::TagHeader; use crate::TagType; +use crate::tag::TagHeader; use core::mem::size_of; use multiboot2_common::{MaybeDynSized, Tag}; diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index bfe66dda..a084cb4c 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -37,9 +37,9 @@ impl ElfSectionsTag { ) } - /// Get an iterator of loaded ELF sections. + /// Get an iterator over the ELF sections. #[must_use] - pub(crate) const fn sections_iter(&self) -> ElfSectionIter { + pub const fn sections(&self) -> ElfSectionIter { let string_section_offset = (self.shndx * self.entry_size) as isize; let string_section_ptr = unsafe { self.sections.as_ptr().offset(string_section_offset) as *const _ }; @@ -96,12 +96,12 @@ impl Debug for ElfSectionsTag { .field("number_of_sections", &self.number_of_sections) .field("entry_size", &self.entry_size) .field("shndx", &self.shndx) - .field("sections", &self.sections_iter()) + .field("sections", &self.sections()) .finish() } } -/// An iterator over some ELF sections. +/// An iterator over [`ElfSection`]s. #[derive(Clone)] pub struct ElfSectionIter<'a> { current_section: *const u8, @@ -132,20 +132,45 @@ impl<'a> Iterator for ElfSectionIter<'a> { } None } + + fn size_hint(&self) -> (usize, Option) { + ( + self.remaining_sections as usize, + Some(self.remaining_sections as usize), + ) + } +} + +impl ExactSizeIterator for ElfSectionIter<'_> { + fn len(&self) -> usize { + self.remaining_sections as usize + } } -impl<'a> Debug for ElfSectionIter<'a> { +impl Debug for ElfSectionIter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + /// Limit how many Elf-Sections should be debug-formatted. + /// Can be thousands of sections for a Rust binary => this is useless output. + /// If the user really wants this, they should debug-format the field directly. + const ELF_SECTIONS_LIMIT: usize = 7; + let mut debug = f.debug_list(); - self.clone().for_each(|ref e| { + + self.clone().take(ELF_SECTIONS_LIMIT).for_each(|ref e| { debug.entry(e); }); + + if self.clone().len() > ELF_SECTIONS_LIMIT { + debug.entry(&"..."); + } + debug.finish() } } /// A single generic ELF Section. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +// TODO Shouldn't this be called ElfSectionPtrs, ElfSectionWrapper or so? +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ElfSection<'a> { inner: *const u8, string_section: *const u8, @@ -153,6 +178,16 @@ pub struct ElfSection<'a> { _phantom: PhantomData<&'a ()>, } +impl Debug for ElfSection<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let inner = self.get(); + f.debug_struct("ElfSection") + .field("inner", &inner) + .field("string_section_ptr", &self.string_section) + .finish() + } +} + #[derive(Clone, Copy, Debug)] #[repr(C, packed)] struct ElfSectionInner32 { @@ -183,8 +218,8 @@ struct ElfSectionInner64 { entry_size: u64, } -impl<'a> ElfSection<'a> { - /// Get the section type as a `ElfSectionType` enum variant. +impl ElfSection<'_> { + /// Get the section type as an `ElfSectionType` enum variant. #[must_use] pub fn section_type(&self) -> ElfSectionType { match self.get().typ() { @@ -203,10 +238,7 @@ impl<'a> ElfSection<'a> { 0x6000_0000..=0x6FFF_FFFF => ElfSectionType::EnvironmentSpecific, 0x7000_0000..=0x7FFF_FFFF => ElfSectionType::ProcessorSpecific, e => { - log::warn!( - "Unknown section type {:x}. Treating as ElfSectionType::Unused", - e - ); + log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused"); ElfSectionType::Unused } } @@ -283,21 +315,28 @@ impl<'a> ElfSection<'a> { match self.entry_size { 40 => unsafe { &*(self.inner as *const ElfSectionInner32) }, 64 => unsafe { &*(self.inner as *const ElfSectionInner64) }, - s => panic!("Unexpected entry size: {}", s), + s => panic!("Unexpected entry size: {s}"), } } unsafe fn string_table(&self) -> *const u8 { - let addr = match self.entry_size { - 40 => (*(self.string_section as *const ElfSectionInner32)).addr as usize, - 64 => (*(self.string_section as *const ElfSectionInner64)).addr as usize, - s => panic!("Unexpected entry size: {}", s), - }; - addr as *const _ + match self.entry_size { + 40 => { + let ptr = self.string_section.cast::(); + let reference = unsafe { ptr.as_ref().unwrap() }; + reference.addr() as *const u8 + } + 64 => { + let ptr = self.string_section.cast::(); + let reference = unsafe { ptr.as_ref().unwrap() }; + reference.addr() as *const u8 + } + s => panic!("Unexpected entry size: {s}"), + } } } -trait ElfSectionInner { +trait ElfSectionInner: Debug { fn name_index(&self) -> u32; fn typ(&self) -> u32; diff --git a/multiboot2/src/end.rs b/multiboot2/src/end.rs index 23707d0b..c2d69b73 100644 --- a/multiboot2/src/end.rs +++ b/multiboot2/src/end.rs @@ -14,7 +14,7 @@ pub struct EndTag { impl Default for EndTag { fn default() -> Self { Self { - header: TagHeader::new(TagType::End, mem::size_of::() as u32), + header: TagHeader::new(Self::ID, mem::size_of::() as u32), } } } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 78b5da0e..8151dccf 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -1,12 +1,12 @@ //! Module for [`FramebufferTag`]. -use crate::tag::TagHeader; use crate::TagType; +use crate::tag::TagHeader; use core::fmt::Debug; use core::mem; use core::slice; -use derive_more::Display; use multiboot2_common::{MaybeDynSized, Tag}; +use thiserror::Error; #[cfg(feature = "builder")] use {alloc::boxed::Box, multiboot2_common::new_boxed}; @@ -22,7 +22,12 @@ impl<'a> Reader<'a> { Self { buffer, off: 0 } } - fn read_u8(&mut self) -> u8 { + /// Reads the next [`u8`] from the buffer and updates the internal pointer. + /// + /// # Panic + /// + /// Panics if the index is out of bounds. + fn read_next_u8(&mut self) -> u8 { let val = self .buffer .get(self.off) @@ -36,8 +41,15 @@ impl<'a> Reader<'a> { val } - fn read_u16(&mut self) -> u16 { - self.read_u8() as u16 | (self.read_u8() as u16) << 8 + /// Reads the next [`u16`] from the buffer and updates the internal pointer. + /// + /// # Panic + /// + /// Panics if the index is out of bounds. + fn read_next_u16(&mut self) -> u16 { + let u16_lo = self.read_next_u8() as u16; + let u16_hi = self.read_next_u8() as u16; + (u16_hi << 8) | u16_lo } const fn current_ptr(&self) -> *const u8 { @@ -166,7 +178,7 @@ impl FramebufferTag { // TODO we can create a struct for this and implement // DynSizedStruct for it to leverage the already existing // functionality - let num_colors = reader.read_u16(); + let num_colors = reader.read_next_u16(); let palette = { // Ensure the slice can be created without causing UB @@ -182,12 +194,12 @@ impl FramebufferTag { Ok(FramebufferType::Indexed { palette }) } FramebufferTypeId::RGB => { - let red_pos = reader.read_u8(); // These refer to the bit positions of the LSB of each field - let red_mask = reader.read_u8(); // And then the length of the field from LSB to MSB - let green_pos = reader.read_u8(); - let green_mask = reader.read_u8(); - let blue_pos = reader.read_u8(); - let blue_mask = reader.read_u8(); + let red_pos = reader.read_next_u8(); // These refer to the bit positions of the LSB of each field + let red_mask = reader.read_next_u8(); // And then the length of the field from LSB to MSB + let green_pos = reader.read_next_u8(); + let green_mask = reader.read_next_u8(); + let blue_pos = reader.read_next_u8(); + let blue_mask = reader.read_next_u8(); Ok(FramebufferType::RGB { red: FramebufferField { position: red_pos, @@ -319,7 +331,7 @@ pub enum FramebufferType<'a> { Text, } -impl<'a> FramebufferType<'a> { +impl FramebufferType<'_> { #[must_use] #[cfg(feature = "builder")] const fn id(&self) -> FramebufferTypeId { @@ -390,13 +402,10 @@ pub struct FramebufferColor { } /// Error when an unknown [`FramebufferTypeId`] is found. -#[derive(Debug, Copy, Clone, Display, PartialEq, Eq)] -#[display("Unknown framebuffer type {}", _0)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)] +#[error("Unknown framebuffer type {0}")] pub struct UnknownFramebufferType(u8); -#[cfg(feature = "unstable")] -impl core::error::Error for UnknownFramebufferType {} - #[cfg(test)] mod tests { use super::*; diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index bb4913d0..560671a1 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -1,7 +1,7 @@ //! Module for [`ImageLoadPhysAddrTag`]. -use crate::tag::TagHeader; use crate::TagType; +use crate::tag::TagHeader; #[cfg(feature = "builder")] use core::mem::size_of; use multiboot2_common::{MaybeDynSized, Tag}; diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index cd13f558..69d3a85b 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![cfg_attr(feature = "unstable", feature(error_in_core))] // --- BEGIN STYLE CHECKS --- #![deny( clippy::all, @@ -42,7 +41,7 @@ //! ``` //! //! ## MSRV -//! The MSRV is 1.75.0 stable. +//! The MSRV is 1.85.0 stable. #[cfg_attr(feature = "builder", macro_use)] #[cfg(feature = "builder")] @@ -115,7 +114,7 @@ pub use rsdp::{RsdpV1Tag, RsdpV2Tag}; pub use smbios::SmbiosTag; pub use tag::TagHeader; pub use tag_type::{TagType, TagTypeId}; -pub use util::{parse_slice_as_string, StringError}; +pub use util::{StringError, parse_slice_as_string}; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, @@ -134,7 +133,7 @@ mod tests { use std::mem; /// Compile time test to check if the boot information is Send and Sync. - /// This test is relevant to give library users flexebility in passing the + /// This test is relevant to give library users flexibility in passing the /// struct around. #[test] fn boot_information_is_send_and_sync() { @@ -168,7 +167,7 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); - assert!(bi.elf_sections().is_none()); + assert!(bi.elf_sections_tag().is_none()); assert!(bi.memory_map_tag().is_none()); assert!(bi.module_tags().next().is_none()); assert!(bi.boot_loader_name_tag().is_none()); @@ -191,7 +190,7 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); - assert!(bi.elf_sections().is_none()); + assert!(bi.elf_sections_tag().is_none()); assert!(bi.memory_map_tag().is_none()); assert!(bi.module_tags().next().is_none()); assert!(bi.boot_loader_name_tag().is_none()); @@ -214,7 +213,7 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); - assert!(bi.elf_sections().is_none()); + assert!(bi.elf_sections_tag().is_none()); assert!(bi.memory_map_tag().is_none()); assert!(bi.module_tags().next().is_none()); assert!(bi.boot_loader_name_tag().is_none()); @@ -240,7 +239,7 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); - assert!(bi.elf_sections().is_none()); + assert!(bi.elf_sections_tag().is_none()); assert!(bi.memory_map_tag().is_none()); assert!(bi.module_tags().next().is_none()); assert_eq!( @@ -834,7 +833,7 @@ mod tests { assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.len(), bi.end_address()); assert_eq!(bytes.len(), bi.total_size()); - let mut es = bi.elf_sections().unwrap(); + let mut es = bi.elf_sections_tag().unwrap().sections(); let s1 = es.next().expect("Should have one more section"); assert_eq!(".rodata", s1.name().expect("Should be valid utf-8")); assert_eq!(0xFFFF_8000_0010_0000, s1.start_address()); @@ -1007,8 +1006,7 @@ mod tests { ]); #[repr(C, align(8))] struct StringBytes([u8; 11]); - let string_bytes: StringBytes = - StringBytes([0, 46, 115, 104, 115, 116, 114, 116, 97, 98, 0]); + let string_bytes: StringBytes = StringBytes(*b"\0.shstrtab\0"); let string_addr = string_bytes.0.as_ptr() as u64; for i in 0..8 { let offset = 108; @@ -1019,10 +1017,13 @@ mod tests { let addr = ptr as usize; let bi = unsafe { BootInformation::load(ptr.cast()) }; let bi = bi.unwrap(); + + eprintln!("boot information with elf sections: {bi:#x?}"); + assert_eq!(addr, bi.start_address()); assert_eq!(addr + bytes.0.len(), bi.end_address()); assert_eq!(bytes.0.len(), bi.total_size()); - let mut es = bi.elf_sections().unwrap(); + let mut es = bi.elf_sections_tag().unwrap().sections(); let s1 = es.next().expect("Should have one more section"); assert_eq!(".shstrtab", s1.name().expect("Should be valid utf-8")); assert_eq!(string_addr, s1.start_address()); @@ -1100,7 +1101,6 @@ mod tests { } #[test] - #[cfg(feature = "unstable")] /// This test succeeds if it compiles. fn mbi_load_error_implements_error() { fn consumer(_e: E) {} diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index a46feb67..07422984 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -422,7 +422,11 @@ impl<'a> EFIMemoryAreaIter<'a> { fn new(mmap_tag: &'a EFIMemoryMapTag) -> Self { let desc_size = mmap_tag.desc_size as usize; let mmap_len = mmap_tag.memory_map.len(); - assert_eq!(mmap_len % desc_size, 0, "memory map length must be a multiple of `desc_size` by definition. The MBI seems to be corrupt."); + assert_eq!( + mmap_len % desc_size, + 0, + "memory map length must be a multiple of `desc_size` by definition. The MBI seems to be corrupt." + ); Self { mmap_tag, i: 0, @@ -455,13 +459,13 @@ impl<'a> Iterator for EFIMemoryAreaIter<'a> { } } -impl<'a> ExactSizeIterator for EFIMemoryAreaIter<'a> { +impl ExactSizeIterator for EFIMemoryAreaIter<'_> { fn len(&self) -> usize { self.entries } } -impl<'a> Debug for EFIMemoryAreaIter<'a> { +impl Debug for EFIMemoryAreaIter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let mut debug = f.debug_list(); let iter = self.clone(); diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 73a334e1..d87ab6f9 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -1,7 +1,7 @@ //! Module for [`ModuleTag`]. use crate::tag::TagHeader; -use crate::{parse_slice_as_string, StringError, TagIter, TagType}; +use crate::{StringError, TagIter, TagType, parse_slice_as_string}; use core::fmt::{Debug, Formatter}; use core::mem; use multiboot2_common::{MaybeDynSized, Tag}; @@ -123,7 +123,7 @@ impl<'a> Iterator for ModuleIter<'a> { } } -impl<'a> Debug for ModuleIter<'a> { +impl Debug for ModuleIter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let mut list = f.debug_list(); self.clone().for_each(|tag| { diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index fbc88005..bd86ec18 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -12,8 +12,8 @@ //! signature should be manually verified. //! -use crate::tag::TagHeader; use crate::TagType; +use crate::tag::TagHeader; #[cfg(feature = "builder")] use core::mem::size_of; use core::slice; diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 0d80b0b0..0eef33d8 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -1,7 +1,7 @@ //! Module for [`SmbiosTag`]. -use crate::tag::TagHeader; use crate::TagType; +use crate::tag::TagHeader; use core::fmt::Debug; use core::mem; use multiboot2_common::{MaybeDynSized, Tag}; diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 3d5921c4..6ad4c334 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -305,7 +305,7 @@ mod tests { set.insert(TagType::LoadBaseAddr); set.insert(TagType::LoadBaseAddr); assert_eq!(set.len(), 4); - println!("{:#?}", set); + println!("{set:#?}"); } #[test] @@ -320,7 +320,7 @@ mod tests { for (current, next) in set.iter().zip(set.iter().skip(1)) { assert!(current < next); } - println!("{:#?}", set); + println!("{set:#?}"); } /// Tests for equality when one type is u32 and the other the enum representation. diff --git a/multiboot2/src/util.rs b/multiboot2/src/util.rs index 31ecc892..f6f6787c 100644 --- a/multiboot2/src/util.rs +++ b/multiboot2/src/util.rs @@ -1,33 +1,18 @@ //! Various utilities. -use core::fmt; -use core::fmt::{Display, Formatter}; use core::str::Utf8Error; +use thiserror::Error; /// Error type describing failures when parsing the string from a tag. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Error)] pub enum StringError { /// There is no terminating NUL character, although the specification /// requires one. - MissingNul(core::ffi::FromBytesUntilNulError), + #[error("string is not null terminated")] + MissingNul(#[source] core::ffi::FromBytesUntilNulError), /// The sequence until the first NUL character is not valid UTF-8. - Utf8(Utf8Error), -} - -impl Display for StringError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -#[cfg(feature = "unstable")] -impl core::error::Error for StringError { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - match self { - Self::MissingNul(e) => Some(e), - Self::Utf8(e) => Some(e), - } - } + #[error("string is not valid UTF-8")] + Utf8(#[source] Utf8Error), } /// Parses the provided byte sequence as Multiboot string, which maps to a @@ -63,7 +48,7 @@ mod tests { // must not include final null assert_eq!(parse_slice_as_string(b"hello\0"), Ok("hello")); assert_eq!(parse_slice_as_string(b"hello\0\0"), Ok("hello")); - // must skip everytihng after first null + // must skip everything after first null assert_eq!(parse_slice_as_string(b"hello\0foo"), Ok("hello")); } } diff --git a/nix/sources.json b/nix/sources.json deleted file mode 100644 index 5da91e3a..00000000 --- a/nix/sources.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "nixpkgs": { - "branch": "nixos-23.11", - "description": "Nix Packages collection", - "homepage": null, - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "576ecd43d3b864966b4423a853412d6177775e8b", - "sha256": "05gr8w09w7fqfi6zflx7df1n0hhsf8f4p9siikk3wb7k7by2d9sr", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/576ecd43d3b864966b4423a853412d6177775e8b.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - } -} diff --git a/nix/sources.nix b/nix/sources.nix deleted file mode 100644 index fe3dadf7..00000000 --- a/nix/sources.nix +++ /dev/null @@ -1,198 +0,0 @@ -# This file has been generated by Niv. - -let - - # - # The fetchers. fetch_ fetches specs of type . - # - - fetch_file = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchurl { inherit (spec) url sha256; name = name'; } - else - pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; - - fetch_tarball = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchTarball { name = name'; inherit (spec) url sha256; } - else - pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; - - fetch_git = name: spec: - let - ref = - spec.ref or ( - if spec ? branch then "refs/heads/${spec.branch}" else - if spec ? tag then "refs/tags/${spec.tag}" else - abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!" - ); - submodules = spec.submodules or false; - submoduleArg = - let - nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0; - emptyArgWithWarning = - if submodules - then - builtins.trace - ( - "The niv input \"${name}\" uses submodules " - + "but your nix's (${builtins.nixVersion}) builtins.fetchGit " - + "does not support them" - ) - { } - else { }; - in - if nixSupportsSubmodules - then { inherit submodules; } - else emptyArgWithWarning; - in - builtins.fetchGit - ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg); - - fetch_local = spec: spec.path; - - fetch_builtin-tarball = name: throw - ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=tarball -a builtin=true''; - - fetch_builtin-url = name: throw - ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=file -a builtin=true''; - - # - # Various helpers - # - - # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 - sanitizeName = name: - ( - concatMapStrings (s: if builtins.isList s then "-" else s) - ( - builtins.split "[^[:alnum:]+._?=-]+" - ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) - ) - ); - - # The set of packages used when specs are fetched using non-builtins. - mkPkgs = sources: system: - let - sourcesNixpkgs = - import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; - hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; - hasThisAsNixpkgsPath = == ./.; - in - if builtins.hasAttr "nixpkgs" sources - then sourcesNixpkgs - else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then - import { } - else - abort - '' - Please specify either (through -I or NIX_PATH=nixpkgs=...) or - add a package called "nixpkgs" to your sources.json. - ''; - - # The actual fetching function. - fetch = pkgs: name: spec: - - if ! builtins.hasAttr "type" spec then - abort "ERROR: niv spec ${name} does not have a 'type' attribute" - else if spec.type == "file" then fetch_file pkgs name spec - else if spec.type == "tarball" then fetch_tarball pkgs name spec - else if spec.type == "git" then fetch_git name spec - else if spec.type == "local" then fetch_local spec - else if spec.type == "builtin-tarball" then fetch_builtin-tarball name - else if spec.type == "builtin-url" then fetch_builtin-url name - else - abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; - - # If the environment variable NIV_OVERRIDE_${name} is set, then use - # the path directly as opposed to the fetched source. - replace = name: drv: - let - saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name; - ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; - in - if ersatz == "" then drv else - # this turns the string into an actual Nix path (for both absolute and - # relative paths) - if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; - - # Ports of functions for older nix versions - - # a Nix version of mapAttrs if the built-in doesn't exist - mapAttrs = builtins.mapAttrs or ( - f: set: with builtins; - listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) - ); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 - range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 - stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 - stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); - concatMapStrings = f: list: concatStrings (map f list); - concatStrings = builtins.concatStringsSep ""; - - # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 - optionalAttrs = cond: as: if cond then as else { }; - - # fetchTarball version that is compatible between all the versions of Nix - builtins_fetchTarball = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchTarball; - in - if lessThan nixVersion "1.12" then - fetchTarball ({ inherit url; } // (optionalAttrs (name != null) { inherit name; })) - else - fetchTarball attrs; - - # fetchurl version that is compatible between all the versions of Nix - builtins_fetchurl = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchurl; - in - if lessThan nixVersion "1.12" then - fetchurl ({ inherit url; } // (optionalAttrs (name != null) { inherit name; })) - else - fetchurl attrs; - - # Create the final "sources" from the config - mkSources = config: - mapAttrs - ( - name: spec: - if builtins.hasAttr "outPath" spec - then - abort - "The values in sources.json should not have an 'outPath' attribute" - else - spec // { outPath = replace name (fetch config.pkgs name spec); } - ) - config.sources; - - # The "config" used by the fetchers - mkConfig = - { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null - , sources ? if sourcesFile == null then { } else builtins.fromJSON (builtins.readFile sourcesFile) - , system ? builtins.currentSystem - , pkgs ? mkPkgs sources system - }: rec { - # The sources, i.e. the attribute set of spec name to spec - inherit sources; - - # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers - inherit pkgs; - }; - -in -mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/shell.nix b/shell.nix index 178c58af..3a03c743 100644 --- a/shell.nix +++ b/shell.nix @@ -1,7 +1,5 @@ -let - sources = import ./nix/sources.nix; - pkgs = import sources.nixpkgs {}; -in +{ pkgs ? import {} }: + pkgs.mkShell rec { packages = with pkgs; [ # general @@ -18,9 +16,4 @@ pkgs.mkShell rec { ./integration-test/run.sh '') ]; - - # To invoke "nix-shell" in the CI-runner, we need a global Nix channel. - # For better reproducibility inside the Nix shell, we override this channel - # with the pinned nixpkgs version. - NIX_PATH = "nixpkgs=${sources.nixpkgs}"; }