From deca5ab339f192023debc748c44c51e75b8269b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 10:27:05 +0000 Subject: [PATCH 01/35] build(deps): bump cachix/install-nix-action from 26 to 27 Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 26 to 27. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v26...V27) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/integrationtest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integrationtest.yml b/.github/workflows/integrationtest.yml index 13de84fd..2386694d 100644 --- a/.github/workflows/integrationtest.yml +++ b/.github/workflows/integrationtest.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Check out uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: # This channel is only required to invoke "nix-shell". # Everything inside that nix-shell will use a pinned version of From 2319829b9d8d72ed223fa97a628a477ede4c905c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:36:25 +0000 Subject: [PATCH 02/35] build(deps): bump crate-ci/typos from 1.21.0 to 1.22.9 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.21.0 to 1.22.9. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.21.0...v1.22.9) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/qa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 02fb711c..894f2eae 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -9,4 +9,4 @@ jobs: steps: - uses: actions/checkout@v4 # Executes "typos ." - - uses: crate-ci/typos@v1.21.0 + - uses: crate-ci/typos@v1.22.9 From bc9e0be8c6998910ba161c8da3dde03d6e260fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:57:35 +0000 Subject: [PATCH 03/35] build(deps): bump bitflags from 2.5.0 to 2.6.0 Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.5.0 to 2.6.0. - [Release notes](https://github.com/bitflags/bitflags/releases) - [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md) - [Commits](https://github.com/bitflags/bitflags/compare/2.5.0...2.6.0) --- updated-dependencies: - dependency-name: bitflags dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a931553..ce8c2c95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" diff --git a/Cargo.toml b/Cargo.toml index f4e5b0ef..76c7c806 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ exclude = [ ] [workspace.dependencies] -bitflags = "2.0.2" +bitflags = "2.6.0" derive_more = { version = "~0.99", default-features = false, features = ["display"] } log = { version = "~0.4", default-features = false } From c1a0d0083a1147d32c05c0d3f7c39dc1355b854f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:59:54 +0000 Subject: [PATCH 04/35] build(deps): bump crate-ci/typos from 1.22.9 to 1.23.6 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.22.9 to 1.23.6. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.22.9...v1.23.6) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/qa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 894f2eae..ed022c2e 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -9,4 +9,4 @@ jobs: steps: - uses: actions/checkout@v4 # Executes "typos ." - - uses: crate-ci/typos@v1.22.9 + - uses: crate-ci/typos@v1.23.6 From fcab92cdb636c6c215e24e3c35279817b741e3cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 10:13:03 +0000 Subject: [PATCH 05/35] build(deps): bump derive_more from 0.99.17 to 0.99.18 Bumps [derive_more](https://github.com/JelteF/derive_more) from 0.99.17 to 0.99.18. - [Release notes](https://github.com/JelteF/derive_more/releases) - [Changelog](https://github.com/JelteF/derive_more/blob/v0.99.18/CHANGELOG.md) - [Commits](https://github.com/JelteF/derive_more/compare/v0.99.17...v0.99.18) --- updated-dependencies: - dependency-name: derive_more dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce8c2c95..0022eb84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,13 +10,13 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.65", ] [[package]] @@ -70,7 +70,7 @@ checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -93,6 +93,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "uefi-raw" version = "0.5.2" From 2ec4cd050184ad8b8646f901731550de49767323 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 12 Aug 2024 13:57:06 +0200 Subject: [PATCH 06/35] cargo: update transitive dependencies --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0022eb84..a17cb6d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,14 +16,14 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.74", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "multiboot2" @@ -46,9 +46,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", From 614462c959fe0bed3e3a9d095fdee60b24e7d13e Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 12 Aug 2024 14:04:28 +0200 Subject: [PATCH 07/35] cargo: update all dependencies --- Cargo.lock | 20 +++++++- Cargo.toml | 2 +- integration-test/bins/Cargo.lock | 78 +++++++++++++++++++------------- integration-test/bins/Cargo.toml | 6 +++ multiboot2/src/framebuffer.rs | 2 +- multiboot2/src/lib.rs | 6 +-- 6 files changed, 75 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a17cb6d5..6e378ffe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,13 +10,23 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" -version = "0.99.18" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +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", ] [[package]] @@ -126,3 +136,9 @@ 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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" diff --git a/Cargo.toml b/Cargo.toml index 76c7c806..e5eacb6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ exclude = [ [workspace.dependencies] bitflags = "2.6.0" -derive_more = { version = "~0.99", default-features = false, features = ["display"] } +derive_more = { version = "1.0.0", default-features = false, features = ["display"] } log = { version = "~0.4", default-features = false } # This way, the "multiboot2" dependency in the multiboot2-header crate can be diff --git a/integration-test/bins/Cargo.lock b/integration-test/bins/Cargo.lock index 261b0100..d87a36f5 100644 --- a/integration-test/bins/Cargo.lock +++ b/integration-test/bins/Cargo.lock @@ -28,26 +28,36 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" -version = "0.99.17" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +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", + "syn 2.0.74", + "unicode-xid", ] [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elf_rs" @@ -55,7 +65,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "894d710b6b07dae25ce69f9227ec2ffa3a3f71dc7f071acea3e1928ab4aeafdf" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "num-traits", ] @@ -81,9 +91,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "multiboot" @@ -96,22 +106,9 @@ dependencies = [ [[package]] name = "multiboot2" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d67e1b461b49127f2226c78a2b4090f72212c44fa27342bcfef93dd39bd6b86" -dependencies = [ - "bitflags 2.5.0", - "derive_more", - "log", - "ptr_meta", - "uefi-raw", -] - -[[package]] -name = "multiboot2" -version = "0.20.1" +version = "0.20.2" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "derive_more", "log", "ptr_meta", @@ -123,7 +120,7 @@ name = "multiboot2-header" version = "0.4.0" dependencies = [ "derive_more", - "multiboot2 0.20.0", + "multiboot2", ] [[package]] @@ -135,7 +132,7 @@ dependencies = [ "good_memory_allocator", "log", "multiboot", - "multiboot2 0.20.1", + "multiboot2", "multiboot2-header", "util", ] @@ -147,7 +144,7 @@ dependencies = [ "anyhow", "good_memory_allocator", "log", - "multiboot2 0.20.1", + "multiboot2", "util", "x86", ] @@ -169,9 +166,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -193,7 +190,7 @@ checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -246,13 +243,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "uefi-raw" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa8716f52e8cab8bcedfd5052388a0f263b69fe5cc2561548dc6a530678333c" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "ptr_meta", "uguid", ] @@ -269,6 +277,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "util" version = "0.1.0" diff --git a/integration-test/bins/Cargo.toml b/integration-test/bins/Cargo.toml index ddd6e60e..335dd53f 100644 --- a/integration-test/bins/Cargo.toml +++ b/integration-test/bins/Cargo.toml @@ -18,3 +18,9 @@ multiboot2 = { path = "../../multiboot2", features = ["builder", "unstable"] } multiboot2-header = { path = "../../multiboot2-header", features = ["builder", "unstable"] } good_memory_allocator = "0.1" util = { path = "./util" } + +# This way, the "multiboot2" dependency in the multiboot2-header crate can be +# referenced by version, while still the repository version is used +# transparently during local development. +[patch.crates-io] +multiboot2 = { path = "../../multiboot2" } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index bb80dc25..5a92b8c4 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -324,7 +324,7 @@ impl AsBytes for FramebufferColor {} /// Error when an unknown [`FramebufferTypeId`] is found. #[derive(Debug, Copy, Clone, Display, PartialEq, Eq)] -#[display(fmt = "Unknown framebuffer type {}", _0)] +#[display("Unknown framebuffer type {}", _0)] pub struct UnknownFramebufferType(u8); #[cfg(feature = "unstable")] diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index af37b5b7..bf3cb729 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -112,15 +112,15 @@ pub const MAGIC: u32 = 0x36d76289; pub enum MbiLoadError { /// The address is invalid. Make sure that the address is 8-byte aligned, /// according to the spec. - #[display(fmt = "The address is invalid")] + #[display("The address is invalid")] IllegalAddress, /// The total size of the multiboot2 information structure must be not zero /// and a multiple of 8. - #[display(fmt = "The size of the MBI is unexpected")] + #[display("The size of the MBI is unexpected")] IllegalTotalSize(u32), /// Missing end tag. Each multiboot2 boot information requires to have an /// end tag. - #[display(fmt = "There is no end tag")] + #[display("There is no end tag")] NoEndTag, } From 484160e832201d683e1a5d0775aafb436609daf6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 12 Aug 2024 15:53:20 +0200 Subject: [PATCH 08/35] msrv: update from 1.70 to 1.75 --- .github/workflows/rust.yml | 6 +++--- multiboot2-header/Cargo.toml | 2 +- multiboot2-header/Changelog.md | 3 +++ multiboot2-header/README.md | 2 +- multiboot2-header/src/lib.rs | 2 +- multiboot2/Cargo.toml | 2 +- multiboot2/Changelog.md | 5 +++++ multiboot2/README.md | 2 +- multiboot2/src/lib.rs | 2 +- 9 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6c9eb137..e0a81671 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,7 +20,7 @@ jobs: name: build (msrv) uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.70.0 # MSRV + rust-version: 1.75.0 # MSRV do-style-check: false features: builder @@ -46,7 +46,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.70.0 # MSRV + rust-version: 1.75.0 # MSRV do-style-check: false rust-target: thumbv7em-none-eabihf features: builder @@ -103,7 +103,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.70.0 # MSRV + rust-version: 1.75.0 # MSRV do-style-check: true do-test: false features: builder diff --git a/multiboot2-header/Cargo.toml b/multiboot2-header/Cargo.toml index 0fd73855..e94b20e7 100644 --- a/multiboot2-header/Cargo.toml +++ b/multiboot2-header/Cargo.toml @@ -26,7 +26,7 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2-header" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2-header" -rust-version = "1.70" +rust-version = "1.75" [[example]] name = "minimal" diff --git a/multiboot2-header/Changelog.md b/multiboot2-header/Changelog.md index 693d8662..55986349 100644 --- a/multiboot2-header/Changelog.md +++ b/multiboot2-header/Changelog.md @@ -2,6 +2,9 @@ ## Unreleased +- updated dependencies +- MSRV is 1.75 + ## 0.4.0 (2024-05-01) - added `EndHeaderTag::default()` diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index eebac8d0..577a75a8 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -77,7 +77,7 @@ bytes of the ELF. See Multiboot2 specification. ## MSRV -The MSRV is 1.70.0 stable. +The MSRV is 1.75.0 stable. ## License & Contribution diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 456f4682..165bace8 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -34,7 +34,7 @@ //! //! ## MSRV //! -//! The MSRV is 1.70.0 stable. +//! The MSRV is 1.75.0 stable. #![no_std] #![cfg_attr(feature = "unstable", feature(error_in_core))] diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index 152d34d2..4a7732de 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -31,7 +31,7 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2" -rust-version = "1.70" +rust-version = "1.75" [features] default = ["builder"] diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 89055385..f949feaa 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -1,5 +1,10 @@ # CHANGELOG for crate `multiboot2` +## Unreleased + +- updated dependencies +- MSRV is 1.75 + ## 0.20.2 (2024-05-26) - fix Debug implementation of `EfiMemoryMapTag` diff --git a/multiboot2/README.md b/multiboot2/README.md index 4bdd1893..52a207e5 100644 --- a/multiboot2/README.md +++ b/multiboot2/README.md @@ -45,7 +45,7 @@ tag_, which is a tag of type `0` and size `8`. ## MSRV -The MSRV is 1.70.0 stable. +The MSRV is 1.75.0 stable. ## License & Contribution diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index bf3cb729..f9b07f35 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -33,7 +33,7 @@ //! ``` //! //! ## MSRV -//! The MSRV is 1.70.0 stable. +//! The MSRV is 1.75.0 stable. #[cfg(feature = "builder")] extern crate alloc; From 4888d5576dc5d48e8ce0f1549cc20fd84818f409 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 08:15:41 +0200 Subject: [PATCH 09/35] ci: also run doc tests cargo nextest still can't run doctests. --- .github/workflows/_build-rust.yml | 15 ++------------- multiboot2/Changelog.md | 1 + multiboot2/src/lib.rs | 5 ++--- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/.github/workflows/_build-rust.yml b/.github/workflows/_build-rust.yml index 2c834c64..27c01a2f 100644 --- a/.github/workflows/_build-rust.yml +++ b/.github/workflows/_build-rust.yml @@ -94,19 +94,8 @@ jobs: run: | cargo doc --no-deps --document-private-items --features ${{ inputs.features }} --no-default-features cargo clippy --all-targets --features ${{ inputs.features }} --no-default-features - - name: Unit Test (UNIX) - if: inputs.do-test && runner.os != 'Windows' - run: | - curl -LsSf https://get.nexte.st/latest/linux | tar zxf - - chmod u+x cargo-nextest - ./cargo-nextest nextest run --features ${{ inputs.features }} --no-default-features - - name: Unit Test (Windows) - if: inputs.do-test && runner.os == 'Windows' - run: | - Invoke-WebRequest https://get.nexte.st/latest/windows -OutFile cargo-nextest.zip - Expand-Archive .\cargo-nextest.zip - cp .\cargo-nextest/cargo-nextest.exe . - .\cargo-nextest.exe nextest run --features ${{ inputs.features }} --no-default-features + - name: Unit Test + run: cargo test --verbose - name: Unit Test with Miri if: inputs.do-miri # "--tests" so that the doctests are skipped. Currently, the doctest diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index f949feaa..1a5cfe99 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -4,6 +4,7 @@ - updated dependencies - MSRV is 1.75 +- doc fixes ## 0.20.2 (2024-05-26) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index f9b07f35..384473de 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -424,8 +424,7 @@ impl<'a> BootInformation<'a> { /// special handling is required. This is reflected by code-comments. /// /// ```no_run - /// use std::str::Utf8Error; - /// use multiboot2::{BootInformation, BootInformationHeader, Tag, TagTrait, TagType, TagTypeId}; + /// use multiboot2::{BootInformation, BootInformationHeader, StringError, Tag, TagTrait, TagType, TagTypeId}; /// /// #[repr(C)] /// #[derive(multiboot2::Pointee)] // Only needed for DSTs. @@ -449,7 +448,7 @@ impl<'a> BootInformation<'a> { /// } /// /// impl CustomTag { - /// fn name(&self) -> Result<&str, Utf8Error> { + /// fn name(&self) -> Result<&str, StringError> { /// Tag::parse_slice_as_string(&self.name) /// } /// } From 767eb0ca18c4255653a210f9dbfc8d2ea14d7738 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 13:42:28 +0200 Subject: [PATCH 10/35] crate: move BootInformation to dedicated module This improves code quality --- multiboot2/src/boot_information.rs | 430 +++++++++++++++++++++++++++++ multiboot2/src/lib.rs | 427 +--------------------------- multiboot2/src/module.rs | 4 +- 3 files changed, 434 insertions(+), 427 deletions(-) create mode 100644 multiboot2/src/boot_information.rs diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs new file mode 100644 index 00000000..8effbc71 --- /dev/null +++ b/multiboot2/src/boot_information.rs @@ -0,0 +1,430 @@ +//! Module for [`BootInformation`]. + +#[cfg(feature = "builder")] +use crate::builder::AsBytes; +use crate::framebuffer::UnknownFramebufferType; +use crate::tag::TagIter; +use crate::{ + module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, + EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, + ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag, + ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag, +}; +use core::fmt; +use derive_more::Display; + +/// Error type that describes errors while loading/parsing a multiboot2 information structure +/// from a given address. +#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MbiLoadError { + /// The address is invalid. Make sure that the address is 8-byte aligned, + /// according to the spec. + #[display("The address is invalid")] + IllegalAddress, + /// The total size of the multiboot2 information structure must be not zero + /// and a multiple of 8. + #[display("The size of the MBI is unexpected")] + IllegalTotalSize(u32), + /// Missing end tag. Each multiboot2 boot information requires to have an + /// end tag. + #[display("There is no end tag")] + NoEndTag, +} + +#[cfg(feature = "unstable")] +impl core::error::Error for MbiLoadError {} + +/// The basic header of a [`BootInformation`] as sized Rust type. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +pub struct BootInformationHeader { + // size is multiple of 8 + pub total_size: u32, + _reserved: u32, + // Followed by the boot information tags. +} + +#[cfg(feature = "builder")] +impl BootInformationHeader { + pub(crate) fn new(total_size: u32) -> Self { + Self { + total_size, + _reserved: 0, + } + } +} + +#[cfg(feature = "builder")] +impl AsBytes for BootInformationHeader {} + +/// This type holds the whole data of the MBI. This helps to better satisfy miri +/// when it checks for memory issues. +#[derive(ptr_meta::Pointee)] +#[repr(C)] +struct BootInformationInner { + header: BootInformationHeader, + tags: [u8], +} + +impl BootInformationInner { + /// Checks if the MBI has a valid end tag by checking the end of the mbi's + /// bytes. + fn has_valid_end_tag(&self) -> bool { + let end_tag_prototype = EndTag::default(); + + let self_ptr = unsafe { self.tags.as_ptr().sub(size_of::()) }; + + let end_tag_ptr = unsafe { + self_ptr + .add(self.header.total_size as usize) + .sub(size_of::()) + }; + let end_tag = unsafe { &*(end_tag_ptr as *const EndTag) }; + + end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size + } +} + +/// A Multiboot 2 Boot Information (MBI) accessor. +#[repr(transparent)] +pub struct BootInformation<'a>(&'a BootInformationInner); + +impl<'a> BootInformation<'a> { + /// Loads the [`BootInformation`] from a pointer. The pointer must be valid + /// and aligned to an 8-byte boundary, as defined by the spec. + /// + /// ## Example + /// + /// ```rust + /// use multiboot2::{BootInformation, BootInformationHeader}; + /// + /// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) { + /// if mb_magic == multiboot2::MAGIC { + /// let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() }; + /// let _cmd = boot_info.command_line_tag(); + /// } else { /* Panic or use multiboot1 flow. */ } + /// } + /// ``` + /// + /// ## Safety + /// * `ptr` must be valid for reading. Otherwise this function might cause + /// invalid machine state or crash your binary (kernel). This can be the + /// case in environments with standard environment (segfault), but also in + /// boot environments, such as UEFI. + /// * The memory at `ptr` must not be modified after calling `load` or the + /// program may observe unsynchronized mutation. + pub unsafe fn load(ptr: *const BootInformationHeader) -> Result { + // null or not aligned + if ptr.is_null() || ptr.align_offset(8) != 0 { + return Err(MbiLoadError::IllegalAddress); + } + + // mbi: reference to basic header + let mbi = &*ptr; + + // Check if total size is not 0 and a multiple of 8. + if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 { + return Err(MbiLoadError::IllegalTotalSize(mbi.total_size)); + } + + let slice_size = mbi.total_size as usize - size_of::(); + // mbi: reference to full mbi + let mbi = ptr_meta::from_raw_parts::(ptr.cast(), slice_size); + let mbi = &*mbi; + + if !mbi.has_valid_end_tag() { + return Err(MbiLoadError::NoEndTag); + } + + Ok(Self(mbi)) + } + + /// Get the start address of the boot info. + pub fn start_address(&self) -> usize { + self.as_ptr() as usize + } + + /// Get the start address of the boot info as pointer. + pub fn as_ptr(&self) -> *const () { + core::ptr::addr_of!(*self.0).cast() + } + + /// Get the end address of the boot info. + /// + /// This is the same as doing: + /// + /// ```rust,no_run + /// # use multiboot2::{BootInformation, BootInformationHeader}; + /// # let ptr = 0xdeadbeef as *const BootInformationHeader; + /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() }; + /// let end_addr = boot_info.start_address() + boot_info.total_size(); + /// ``` + pub fn end_address(&self) -> usize { + self.start_address() + self.total_size() + } + + /// Get the total size of the boot info struct. + pub fn total_size(&self) -> usize { + self.0.header.total_size as usize + } + + // ###################################################### + // ### BEGIN OF TAG GETTERS (in alphabetical order) + + /*fn apm(&self) { + // also add to debug output + todo!() + }*/ + + /// Search for the basic memory info tag. + pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> { + self.get_tag::() + } + + /// Search for the BootLoader name tag. + pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> { + self.get_tag::() + } + + /*fn bootdev(&self) { + // also add to debug output + todo!() + }*/ + + /// Search for the Command line tag. + pub fn command_line_tag(&self) -> Option<&CommandLineTag> { + self.get_tag::() + } + + /// Search for the EFI boot services not exited tag. + pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> { + self.get_tag::() + } + + /// Search for the EFI Memory map tag, if the boot services were exited. + /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None` + /// as it is strictly recommended to get the memory map from the `uefi` + /// services. + pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> { + // If the EFIBootServicesNotExited is present, then we should not use + // the memory map, as it could still be in use. + match self.get_tag::() { + Some(_tag) => { + log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None."); + None + } + None => self.get_tag::(), + } + } + + /// Search for the EFI 32-bit SDT tag. + pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> { + self.get_tag::() + } + + /// Search for the EFI 64-bit SDT tag. + pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> { + self.get_tag::() + } + + /// Search for the EFI 32-bit image handle pointer tag. + pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> { + self.get_tag::() + } + + /// Search for the EFI 64-bit image handle pointer tag. + pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> { + self.get_tag::() + } + + /// Returns an [`ElfSectionIter`] iterator over the ELF Sections, if the + /// [`ElfSectionsTag`] is present. + /// + /// # Examples + /// + /// ```rust,no_run + /// # 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() { + /// let mut total = 0; + /// for section in sections { + /// println!("Section: {:?}", section); + /// total += 1; + /// } + /// } + /// ``` + pub fn elf_sections(&self) -> Option { + let tag = self.get_tag::(); + tag.map(|t| { + assert!((t.entry_size * t.shndx) <= t.size); + t.sections() + }) + } + + /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the + /// framebuffer type is unknown, while the framebuffer tag is present. + pub fn framebuffer_tag(&self) -> Option> { + self.get_tag::() + .map(|tag| match tag.buffer_type() { + Ok(_) => Ok(tag), + Err(e) => Err(e), + }) + } + + /// Search for the Image Load Base Physical Address tag. + pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> { + self.get_tag::() + } + + /// Search for the Memory map tag. + pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> { + self.get_tag::() + } + + /// Get an iterator of all module tags. + pub fn module_tags(&self) -> ModuleIter { + module::module_iter(self.tags()) + } + + /*fn network_tag(&self) { + // also add to debug output + todo!() + }*/ + + /// Search for the (ACPI 1.0) RSDP tag. + pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> { + self.get_tag::() + } + + /// Search for the (ACPI 2.0 or later) RSDP tag. + pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> { + self.get_tag::() + } + + /// Search for the SMBIOS tag. + pub fn smbios_tag(&self) -> Option<&SmbiosTag> { + self.get_tag::() + } + + /// Search for the VBE information tag. + pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> { + self.get_tag::() + } + + // ### END OF TAG GETTERS + // ###################################################### + + /// Public getter to find any Multiboot tag by its type, including + /// specified and custom ones. + /// + /// # Specified or Custom Tags + /// The Multiboot2 specification specifies a list of tags, see [`TagType`]. + /// However, it doesn't forbid to use custom tags. Because of this, there + /// exists the [`TagType`] abstraction. It is recommended to use this + /// getter only for custom tags. For specified tags, use getters, such as + /// [`Self::efi_ih64_tag`]. + /// + /// ## Use Custom Tags + /// The following example shows how you may use this interface to parse + /// custom tags from the MBI. If they are dynamically sized (DST), a few more + /// special handling is required. This is reflected by code-comments. + /// + /// ```no_run + /// use multiboot2::{BootInformation, BootInformationHeader, StringError, Tag, TagTrait, TagType, TagTypeId}; + /// + /// #[repr(C)] + /// #[derive(multiboot2::Pointee)] // Only needed for DSTs. + /// struct CustomTag { + /// tag: TagTypeId, + /// size: u32, + /// // begin of inline string + /// name: [u8], + /// } + /// + /// // This implementation is only necessary for tags that are DSTs. + /// impl TagTrait for CustomTag { + /// const ID: TagType = TagType::Custom(0x1337); + /// + /// fn dst_size(base_tag: &Tag) -> usize { + /// // The size of the sized portion of the custom tag. + /// let tag_base_size = 8; // id + size is 8 byte in size + /// assert!(base_tag.size >= 8); + /// base_tag.size as usize - tag_base_size + /// } + /// } + /// + /// impl CustomTag { + /// fn name(&self) -> Result<&str, StringError> { + /// Tag::parse_slice_as_string(&self.name) + /// } + /// } + /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader; + /// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() }; + /// + /// let tag = mbi + /// .get_tag::() + /// .unwrap(); + /// assert_eq!(tag.name(), Ok("name")); + /// ``` + pub fn get_tag(&'a self) -> Option<&'a TagT> { + self.tags() + .find(|tag| tag.typ == TagT::ID) + .map(|tag| tag.cast_tag::()) + } + + /// Returns an iterator over all tags. + fn tags(&self) -> TagIter { + TagIter::new(&self.0.tags) + } +} + +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("basic_memory_info", &(self.basic_memory_info_tag())) + .field("boot_loader_name", &self.boot_loader_name_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_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().unwrap_or_default()); + } + } + + debug + .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("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()) + .finish() + } +} diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 384473de..436a632c 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -49,6 +49,7 @@ extern crate bitflags; #[cfg(feature = "builder")] pub mod builder; +mod boot_information; mod boot_loader_name; mod command_line; mod efi; @@ -65,6 +66,7 @@ mod tag_trait; mod tag_type; mod vbe_info; +pub use boot_information::{BootInformation, BootInformationHeader, MbiLoadError}; pub use boot_loader_name::BootLoaderNameTag; pub use command_line::CommandLineTag; pub use efi::{ @@ -92,436 +94,11 @@ pub use vbe_info::{ VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, }; -use core::fmt; -use core::mem::size_of; -use derive_more::Display; -// Must be public so that custom tags can be DSTs. -#[cfg(feature = "builder")] -use crate::builder::AsBytes; -use crate::framebuffer::UnknownFramebufferType; -use tag::TagIter; - /// Magic number that a Multiboot2-compliant boot loader will use to identify /// the handoff. The location depends on the architecture and the targeted /// machine state. pub const MAGIC: u32 = 0x36d76289; -/// Error type that describes errors while loading/parsing a multiboot2 information structure -/// from a given address. -#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum MbiLoadError { - /// The address is invalid. Make sure that the address is 8-byte aligned, - /// according to the spec. - #[display("The address is invalid")] - IllegalAddress, - /// The total size of the multiboot2 information structure must be not zero - /// and a multiple of 8. - #[display("The size of the MBI is unexpected")] - IllegalTotalSize(u32), - /// Missing end tag. Each multiboot2 boot information requires to have an - /// end tag. - #[display("There is no end tag")] - NoEndTag, -} - -#[cfg(feature = "unstable")] -impl core::error::Error for MbiLoadError {} - -/// The basic header of a boot information. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -pub struct BootInformationHeader { - // size is multiple of 8 - pub total_size: u32, - _reserved: u32, - // Followed by the boot information tags. -} - -#[cfg(feature = "builder")] -impl BootInformationHeader { - fn new(total_size: u32) -> Self { - Self { - total_size, - _reserved: 0, - } - } -} - -#[cfg(feature = "builder")] -impl AsBytes for BootInformationHeader {} - -/// This type holds the whole data of the MBI. This helps to better satisfy miri -/// when it checks for memory issues. -#[derive(ptr_meta::Pointee)] -#[repr(C)] -struct BootInformationInner { - header: BootInformationHeader, - tags: [u8], -} - -impl BootInformationInner { - /// Checks if the MBI has a valid end tag by checking the end of the mbi's - /// bytes. - fn has_valid_end_tag(&self) -> bool { - let end_tag_prototype = EndTag::default(); - - let self_ptr = unsafe { self.tags.as_ptr().sub(size_of::()) }; - - let end_tag_ptr = unsafe { - self_ptr - .add(self.header.total_size as usize) - .sub(size_of::()) - }; - let end_tag = unsafe { &*(end_tag_ptr as *const EndTag) }; - - end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size - } -} - -/// A Multiboot 2 Boot Information (MBI) accessor. -#[repr(transparent)] -pub struct BootInformation<'a>(&'a BootInformationInner); - -impl<'a> BootInformation<'a> { - /// Loads the [`BootInformation`] from a pointer. The pointer must be valid - /// and aligned to an 8-byte boundary, as defined by the spec. - /// - /// ## Example - /// - /// ```rust - /// use multiboot2::{BootInformation, BootInformationHeader}; - /// - /// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) { - /// if mb_magic == multiboot2::MAGIC { - /// let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() }; - /// let _cmd = boot_info.command_line_tag(); - /// } else { /* Panic or use multiboot1 flow. */ } - /// } - /// ``` - /// - /// ## Safety - /// * `ptr` must be valid for reading. Otherwise this function might cause - /// invalid machine state or crash your binary (kernel). This can be the - /// case in environments with standard environment (segfault), but also in - /// boot environments, such as UEFI. - /// * The memory at `ptr` must not be modified after calling `load` or the - /// program may observe unsynchronized mutation. - pub unsafe fn load(ptr: *const BootInformationHeader) -> Result { - // null or not aligned - if ptr.is_null() || ptr.align_offset(8) != 0 { - return Err(MbiLoadError::IllegalAddress); - } - - // mbi: reference to basic header - let mbi = &*ptr; - - // Check if total size is not 0 and a multiple of 8. - if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 { - return Err(MbiLoadError::IllegalTotalSize(mbi.total_size)); - } - - let slice_size = mbi.total_size as usize - size_of::(); - // mbi: reference to full mbi - let mbi = ptr_meta::from_raw_parts::(ptr.cast(), slice_size); - let mbi = &*mbi; - - if !mbi.has_valid_end_tag() { - return Err(MbiLoadError::NoEndTag); - } - - Ok(Self(mbi)) - } - - /// Get the start address of the boot info. - pub fn start_address(&self) -> usize { - self.as_ptr() as usize - } - - /// Get the start address of the boot info as pointer. - pub fn as_ptr(&self) -> *const () { - core::ptr::addr_of!(*self.0).cast() - } - - /// Get the end address of the boot info. - /// - /// This is the same as doing: - /// - /// ```rust,no_run - /// # use multiboot2::{BootInformation, BootInformationHeader}; - /// # let ptr = 0xdeadbeef as *const BootInformationHeader; - /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() }; - /// let end_addr = boot_info.start_address() + boot_info.total_size(); - /// ``` - pub fn end_address(&self) -> usize { - self.start_address() + self.total_size() - } - - /// Get the total size of the boot info struct. - pub fn total_size(&self) -> usize { - self.0.header.total_size as usize - } - - // ###################################################### - // ### BEGIN OF TAG GETTERS (in alphabetical order) - - /*fn apm(&self) { - // also add to debug output - todo!() - }*/ - - /// Search for the basic memory info tag. - pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> { - self.get_tag::() - } - - /// Search for the BootLoader name tag. - pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> { - self.get_tag::() - } - - /*fn bootdev(&self) { - // also add to debug output - todo!() - }*/ - - /// Search for the Command line tag. - pub fn command_line_tag(&self) -> Option<&CommandLineTag> { - self.get_tag::() - } - - /// Search for the EFI boot services not exited tag. - pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> { - self.get_tag::() - } - - /// Search for the EFI Memory map tag, if the boot services were exited. - /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None` - /// as it is strictly recommended to get the memory map from the `uefi` - /// services. - pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> { - // If the EFIBootServicesNotExited is present, then we should not use - // the memory map, as it could still be in use. - match self.get_tag::() { - Some(_tag) => { - log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None."); - None - } - None => self.get_tag::(), - } - } - - /// Search for the EFI 32-bit SDT tag. - pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> { - self.get_tag::() - } - - /// Search for the EFI 64-bit SDT tag. - pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> { - self.get_tag::() - } - - /// Search for the EFI 32-bit image handle pointer tag. - pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> { - self.get_tag::() - } - - /// Search for the EFI 64-bit image handle pointer tag. - pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> { - self.get_tag::() - } - - /// Returns an [`ElfSectionIter`] iterator over the ELF Sections, if the - /// [`ElfSectionsTag`] is present. - /// - /// # Examples - /// - /// ```rust,no_run - /// # 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() { - /// let mut total = 0; - /// for section in sections { - /// println!("Section: {:?}", section); - /// total += 1; - /// } - /// } - /// ``` - pub fn elf_sections(&self) -> Option { - let tag = self.get_tag::(); - tag.map(|t| { - assert!((t.entry_size * t.shndx) <= t.size); - t.sections() - }) - } - - /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the - /// framebuffer type is unknown, while the framebuffer tag is present. - pub fn framebuffer_tag(&self) -> Option> { - self.get_tag::() - .map(|tag| match tag.buffer_type() { - Ok(_) => Ok(tag), - Err(e) => Err(e), - }) - } - - /// Search for the Image Load Base Physical Address tag. - pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> { - self.get_tag::() - } - - /// Search for the Memory map tag. - pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> { - self.get_tag::() - } - - /// Get an iterator of all module tags. - pub fn module_tags(&self) -> ModuleIter { - module::module_iter(self.tags()) - } - - /*fn network_tag(&self) { - // also add to debug output - todo!() - }*/ - - /// Search for the (ACPI 1.0) RSDP tag. - pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> { - self.get_tag::() - } - - /// Search for the (ACPI 2.0 or later) RSDP tag. - pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> { - self.get_tag::() - } - - /// Search for the SMBIOS tag. - pub fn smbios_tag(&self) -> Option<&SmbiosTag> { - self.get_tag::() - } - - /// Search for the VBE information tag. - pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> { - self.get_tag::() - } - - // ### END OF TAG GETTERS - // ###################################################### - - /// Public getter to find any Multiboot tag by its type, including - /// specified and custom ones. - /// - /// # Specified or Custom Tags - /// The Multiboot2 specification specifies a list of tags, see [`TagType`]. - /// However, it doesn't forbid to use custom tags. Because of this, there - /// exists the [`TagType`] abstraction. It is recommended to use this - /// getter only for custom tags. For specified tags, use getters, such as - /// [`Self::efi_ih64_tag`]. - /// - /// ## Use Custom Tags - /// The following example shows how you may use this interface to parse - /// custom tags from the MBI. If they are dynamically sized (DST), a few more - /// special handling is required. This is reflected by code-comments. - /// - /// ```no_run - /// use multiboot2::{BootInformation, BootInformationHeader, StringError, Tag, TagTrait, TagType, TagTypeId}; - /// - /// #[repr(C)] - /// #[derive(multiboot2::Pointee)] // Only needed for DSTs. - /// struct CustomTag { - /// tag: TagTypeId, - /// size: u32, - /// // begin of inline string - /// name: [u8], - /// } - /// - /// // This implementation is only necessary for tags that are DSTs. - /// impl TagTrait for CustomTag { - /// const ID: TagType = TagType::Custom(0x1337); - /// - /// fn dst_size(base_tag: &Tag) -> usize { - /// // The size of the sized portion of the custom tag. - /// let tag_base_size = 8; // id + size is 8 byte in size - /// assert!(base_tag.size >= 8); - /// base_tag.size as usize - tag_base_size - /// } - /// } - /// - /// impl CustomTag { - /// fn name(&self) -> Result<&str, StringError> { - /// Tag::parse_slice_as_string(&self.name) - /// } - /// } - /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader; - /// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() }; - /// - /// let tag = mbi - /// .get_tag::() - /// .unwrap(); - /// assert_eq!(tag.name(), Ok("name")); - /// ``` - pub fn get_tag(&'a self) -> Option<&'a TagT> { - self.tags() - .find(|tag| tag.typ == TagT::ID) - .map(|tag| tag.cast_tag::()) - } - - /// Returns an iterator over all tags. - fn tags(&self) -> TagIter { - TagIter::new(&self.0.tags) - } -} - -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("basic_memory_info", &(self.basic_memory_info_tag())) - .field("boot_loader_name", &self.boot_loader_name_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_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().unwrap_or_default()); - } - } - - debug - .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("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()) - .finish() - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index b5098beb..e8a74bce 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -1,7 +1,7 @@ //! Module for [`ModuleTag`]. -use crate::tag::StringError; -use crate::{Tag, TagIter, TagTrait, TagType, TagTypeId}; +use crate::tag::{StringError, TagIter}; +use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; use core::mem::size_of; #[cfg(feature = "builder")] From ec05bf5663335259dd1a17e4410f52e2fd11e087 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 15:00:13 +0200 Subject: [PATCH 11/35] multiboot2: introduce new TagHeader type This is a preparation for following refactorings. --- multiboot2/Changelog.md | 2 ++ multiboot2/src/boot_loader_name.rs | 9 ++++---- multiboot2/src/command_line.rs | 10 ++++----- multiboot2/src/efi.rs | 32 ++++++++++------------------- multiboot2/src/framebuffer.rs | 33 +++++++++++++++--------------- multiboot2/src/image_load_addr.rs | 9 ++++---- multiboot2/src/memory_map.rs | 17 +++++++-------- multiboot2/src/module.rs | 9 ++++---- multiboot2/src/rsdp.rs | 15 ++++++-------- multiboot2/src/smbios.rs | 14 ++++++------- multiboot2/src/tag.rs | 23 +++++++++++++++++++++ 11 files changed, 88 insertions(+), 85 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 1a5cfe99..5d269291 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -5,6 +5,8 @@ - updated dependencies - MSRV is 1.75 - doc fixes +- Introduced new `TagHeader` type as replacement for the `Tag` type that will + be changed in the next step. ## 0.20.2 (2024-05-26) diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 93b12e0d..3cd2ac5c 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,6 +1,6 @@ //! Module for [`BootLoaderNameTag`]. -use crate::tag::StringError; +use crate::tag::{StringError, TagHeader}; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -13,8 +13,7 @@ const METADATA_SIZE: usize = size_of::() + size_of::(); #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct BootLoaderNameTag { - typ: TagTypeId, - size: u32, + header: TagHeader, /// Null-terminated UTF-8 string name: [u8], } @@ -55,8 +54,8 @@ impl BootLoaderNameTag { impl Debug for BootLoaderNameTag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("BootLoaderNameTag") - .field("typ", &{ self.typ }) - .field("size", &{ self.size }) + .field("typ", &self.header.typ) + .field("size", &self.header.size) .field("name", &self.name()) .finish() } diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index e2b168f8..25bd884a 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -1,8 +1,7 @@ //! Module for [`CommandLineTag`]. +use crate::tag::{StringError, TagHeader}; use crate::{Tag, TagTrait, TagType, TagTypeId}; - -use crate::tag::StringError; use core::fmt::{Debug, Formatter}; use core::mem; use core::str; @@ -18,8 +17,7 @@ pub(crate) const METADATA_SIZE: usize = mem::size_of::() + mem::size_ #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct CommandLineTag { - typ: TagTypeId, - size: u32, + header: TagHeader, /// Null-terminated UTF-8 string cmdline: [u8], } @@ -63,8 +61,8 @@ impl CommandLineTag { impl Debug for CommandLineTag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("CommandLineTag") - .field("typ", &{ self.typ }) - .field("size", &{ self.size }) + .field("typ", &self.header.typ) + .field("size", &self.header.size) .field("cmdline", &self.cmdline()) .finish() } diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index 230b1bd8..acc8fbed 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -6,7 +6,7 @@ //! - [`EFIImageHandle64Tag`] //! - [`EFIBootServicesNotExitedTag`] -use crate::TagTypeId; +use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType}; use core::mem::size_of; @@ -14,8 +14,7 @@ use core::mem::size_of; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFISdt32Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, pointer: u32, } @@ -23,8 +22,7 @@ impl EFISdt32Tag { /// Create a new tag to pass the EFI32 System Table pointer. pub fn new(pointer: u32) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), pointer, } } @@ -45,8 +43,7 @@ impl TagTrait for EFISdt32Tag { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFISdt64Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, pointer: u64, } @@ -54,8 +51,7 @@ impl EFISdt64Tag { /// Create a new tag to pass the EFI64 System Table pointer. pub fn new(pointer: u64) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), pointer, } } @@ -77,8 +73,7 @@ impl TagTrait for EFISdt64Tag { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFIImageHandle32Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, pointer: u32, } @@ -86,8 +81,7 @@ impl EFIImageHandle32Tag { #[cfg(feature = "builder")] pub fn new(pointer: u32) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), pointer, } } @@ -109,8 +103,7 @@ impl TagTrait for EFIImageHandle32Tag { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFIImageHandle64Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, pointer: u64, } @@ -118,8 +111,7 @@ impl EFIImageHandle64Tag { #[cfg(feature = "builder")] pub fn new(pointer: u64) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), pointer, } } @@ -140,8 +132,7 @@ impl TagTrait for EFIImageHandle64Tag { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFIBootServicesNotExitedTag { - typ: TagTypeId, - size: u32, + header: TagHeader, } impl EFIBootServicesNotExitedTag { @@ -155,8 +146,7 @@ impl EFIBootServicesNotExitedTag { impl Default for EFIBootServicesNotExitedTag { fn default() -> Self { Self { - typ: TagType::EfiBs.into(), - size: core::mem::size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), } } } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 5a92b8c4..96b1c32b 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -1,5 +1,6 @@ //! Module for [`FramebufferTag`]. +use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::Debug; use core::mem::size_of; @@ -51,8 +52,7 @@ const METADATA_SIZE: usize = size_of::() #[derive(ptr_meta::Pointee, Eq)] #[repr(C)] pub struct FramebufferTag { - typ: TagTypeId, - size: u32, + header: TagHeader, /// Contains framebuffer physical address. /// @@ -185,13 +185,13 @@ impl TagTrait for FramebufferTag { impl Debug for FramebufferTag { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("FramebufferTag") - .field("typ", &{ self.typ }) - .field("size", &{ self.size }) + .field("typ", &self.header.typ) + .field("size", &self.header.size) .field("buffer_type", &self.buffer_type()) - .field("address", &{ self.address }) - .field("pitch", &{ self.pitch }) - .field("width", &{ self.width }) - .field("height", &{ self.height }) + .field("address", &self.address) + .field("pitch", &self.pitch) + .field("width", &self.width) + .field("height", &self.height) .field("bpp", &self.bpp) .finish() } @@ -199,15 +199,14 @@ impl Debug for FramebufferTag { impl PartialEq for FramebufferTag { fn eq(&self, other: &Self) -> bool { - ({ self.typ } == { other.typ } - && { self.size } == { other.size } - && { self.address } == { other.address } - && { self.pitch } == { other.pitch } - && { self.width } == { other.width } - && { self.height } == { other.height } - && { self.bpp } == { other.bpp } - && { self.type_no } == { other.type_no } - && self.buffer == other.buffer) + self.header == other.header + && self.address == { other.address } + && self.pitch == { other.pitch } + && self.width == { other.width } + && self.height == { other.height } + && self.bpp == { other.bpp } + && self.type_no == { other.type_no } + && self.buffer == other.buffer } } diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index fbc6645d..ae2fe995 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -1,6 +1,7 @@ //! Module for [`ImageLoadPhysAddrTag`]. -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::tag::TagHeader; +use crate::{Tag, TagTrait, TagType}; #[cfg(feature = "builder")] use core::mem::size_of; @@ -10,8 +11,7 @@ use core::mem::size_of; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct ImageLoadPhysAddrTag { - typ: TagTypeId, - size: u32, + header: TagHeader, load_base_addr: u32, } @@ -19,8 +19,7 @@ impl ImageLoadPhysAddrTag { #[cfg(feature = "builder")] pub fn new(load_base_addr: u32) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), load_base_addr, } } diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index 3b005c09..f7989898 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -5,6 +5,7 @@ pub use uefi_raw::table::boot::MemoryAttribute as EFIMemoryAttribute; pub use uefi_raw::table::boot::MemoryDescriptor as EFIMemoryDesc; pub use uefi_raw::table::boot::MemoryType as EFIMemoryAreaType; +use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; @@ -27,8 +28,7 @@ const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of:: for MemoryAreaType { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct BasicMemoryInfoTag { - typ: TagTypeId, - size: u32, + header: TagHeader, memory_lower: u32, memory_upper: u32, } @@ -255,8 +254,7 @@ pub struct BasicMemoryInfoTag { impl BasicMemoryInfoTag { pub fn new(memory_lower: u32, memory_upper: u32) -> Self { Self { - typ: Self::ID.into(), - size: mem::size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), memory_lower, memory_upper, } @@ -287,8 +285,7 @@ impl AsBytes for EFIMemoryDesc {} #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EFIMemoryMapTag { - typ: TagTypeId, - size: u32, + header: TagHeader, /// Most likely a little more than the size of a [`EFIMemoryDesc`]. /// This is always the reference, and `size_of` never. /// See . @@ -380,8 +377,8 @@ impl EFIMemoryMapTag { impl Debug for EFIMemoryMapTag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("EFIMemoryMapTag") - .field("typ", &self.typ) - .field("size", &self.size) + .field("typ", &self.header.typ) + .field("size", &self.header.size) .field("desc_size", &self.desc_size) .field("buf", &self.memory_map.as_ptr()) .field("buf_len", &self.memory_map.len()) diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index e8a74bce..8359cbc5 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -1,6 +1,6 @@ //! Module for [`ModuleTag`]. -use crate::tag::{StringError, TagIter}; +use crate::tag::{StringError, TagHeader, TagIter}; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -15,8 +15,7 @@ const METADATA_SIZE: usize = size_of::() + 3 * size_of::(); #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct ModuleTag { - typ: TagTypeId, - size: u32, + header: TagHeader, mod_start: u32, mod_end: u32, /// Null-terminated UTF-8 string @@ -81,8 +80,8 @@ impl TagTrait for ModuleTag { impl Debug for ModuleTag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("ModuleTag") - .field("type", &{ self.typ }) - .field("size", &{ self.size }) + .field("type", &self.header.typ) + .field("size", &self.header.size) // Trick to print as hex. .field("mod_start", &self.mod_start) .field("mod_end", &self.mod_end) diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index d8526157..4d9ca2aa 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -12,7 +12,8 @@ //! signature should be manually verified. //! -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::tag::TagHeader; +use crate::{Tag, TagTrait, TagType}; #[cfg(feature = "builder")] use core::mem::size_of; use core::slice; @@ -25,8 +26,7 @@ const RSDPV1_LENGTH: usize = 20; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct RsdpV1Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, signature: [u8; 8], checksum: u8, oem_id: [u8; 6], @@ -44,8 +44,7 @@ impl RsdpV1Tag { rsdt_address: u32, ) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), signature, checksum, oem_id, @@ -97,8 +96,7 @@ impl TagTrait for RsdpV1Tag { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct RsdpV2Tag { - typ: TagTypeId, - size: u32, + header: TagHeader, signature: [u8; 8], checksum: u8, oem_id: [u8; 6], @@ -125,8 +123,7 @@ impl RsdpV2Tag { ext_checksum: u8, ) -> Self { Self { - typ: Self::ID.into(), - size: size_of::().try_into().unwrap(), + header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), signature, checksum, oem_id, diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 5c34c7d8..46f1ad33 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -2,6 +2,7 @@ #[cfg(feature = "builder")] use crate::builder::BoxedDst; +use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::Debug; @@ -13,8 +14,7 @@ const METADATA_SIZE: usize = core::mem::size_of::() #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct SmbiosTag { - typ: TagTypeId, - size: u32, + header: TagHeader, pub major: u8, pub minor: u8, _reserved: [u8; 6], @@ -42,10 +42,10 @@ impl TagTrait for SmbiosTag { impl Debug for SmbiosTag { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("BootLoaderNameTag") - .field("typ", &{ self.typ }) - .field("size", &{ self.size }) - .field("major", &{ self.major }) - .field("minor", &{ self.minor }) + .field("typ", &self.header.typ) + .field("size", &self.header.size) + .field("major", &self.major) + .field("minor", &self.minor) .finish() } } @@ -76,7 +76,7 @@ mod tests { let tag = get_bytes(); let tag = unsafe { &*tag.as_ptr().cast::() }; let tag = tag.cast_tag::(); - assert_eq!({ tag.typ }, TagType::Smbios); + assert_eq!(tag.header.typ, TagType::Smbios); assert_eq!(tag.major, 3); assert_eq!(tag.minor, 0); assert_eq!(tag.tables, [0xabu8; 24]); diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 0002d59f..8909e6ee 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -34,6 +34,29 @@ impl core::error::Error for StringError { } } +/// The common header that all tags have in common. This type is ABI compatible. +/// +/// Not to be confused with Multiboot header tags, which are something +/// different. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(C)] +pub struct TagHeader { + pub typ: TagTypeId, /* u32 */ + pub size: u32, + // Followed by additional, tag specific fields. +} + +impl TagHeader { + /// Creates a new header. + #[cfg(feature = "builder")] + pub fn new(typ: impl Into, size: u32) -> Self { + Self { + typ: typ.into(), + size, + } + } +} + /// Common base structure for all tags that can be passed via the Multiboot2 /// Information Structure (MBI) to a Multiboot2 payload/program/kernel. /// From 34abe94b4afe995b429d3e89ecd26ced532e102a Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 15:07:45 +0200 Subject: [PATCH 12/35] multiboot2: apply latest clippy code style guidelines --- multiboot2/Changelog.md | 1 + multiboot2/src/boot_information.rs | 41 +++++++++++++++----- multiboot2/src/boot_loader_name.rs | 3 +- multiboot2/src/builder/boxed_dst.rs | 2 +- multiboot2/src/builder/information.rs | 22 ++++++++++- multiboot2/src/command_line.rs | 5 ++- multiboot2/src/efi.rs | 17 +++++++-- multiboot2/src/elf_sections.rs | 13 ++++++- multiboot2/src/framebuffer.rs | 20 ++++++---- multiboot2/src/image_load_addr.rs | 4 +- multiboot2/src/lib.rs | 20 +++++++--- multiboot2/src/memory_map.rs | 32 +++++++++++----- multiboot2/src/module.rs | 14 ++++--- multiboot2/src/rsdp.rs | 27 +++++++++----- multiboot2/src/smbios.rs | 1 + multiboot2/src/tag.rs | 6 ++- multiboot2/src/tag_trait.rs | 1 + multiboot2/src/tag_type.rs | 54 ++++++++++++++------------- 18 files changed, 198 insertions(+), 85 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 5d269291..6e28ec80 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -2,6 +2,7 @@ ## Unreleased +- **Breaking** All functions that returns something useful are now `#[must_use]` - updated dependencies - MSRV is 1.75 - doc fixes diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 8effbc71..affd4e9d 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -46,7 +46,7 @@ pub struct BootInformationHeader { #[cfg(feature = "builder")] impl BootInformationHeader { - pub(crate) fn new(total_size: u32) -> Self { + pub(crate) const fn new(total_size: u32) -> Self { Self { total_size, _reserved: 0, @@ -140,12 +140,14 @@ impl<'a> BootInformation<'a> { } /// Get the start address of the boot info. + #[must_use] pub fn start_address(&self) -> usize { self.as_ptr() as usize } /// Get the start address of the boot info as pointer. - pub fn as_ptr(&self) -> *const () { + #[must_use] + pub const fn as_ptr(&self) -> *const () { core::ptr::addr_of!(*self.0).cast() } @@ -159,12 +161,14 @@ impl<'a> BootInformation<'a> { /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() }; /// let end_addr = boot_info.start_address() + boot_info.total_size(); /// ``` + #[must_use] pub fn end_address(&self) -> usize { self.start_address() + self.total_size() } /// Get the total size of the boot info struct. - pub fn total_size(&self) -> usize { + #[must_use] + pub const fn total_size(&self) -> usize { self.0.header.total_size as usize } @@ -177,11 +181,13 @@ impl<'a> BootInformation<'a> { }*/ /// Search for the basic memory info tag. + #[must_use] pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> { self.get_tag::() } /// Search for the BootLoader name tag. + #[must_use] pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> { self.get_tag::() } @@ -192,11 +198,13 @@ impl<'a> BootInformation<'a> { }*/ /// Search for the Command line tag. + #[must_use] pub fn command_line_tag(&self) -> Option<&CommandLineTag> { self.get_tag::() } /// Search for the EFI boot services not exited tag. + #[must_use] pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> { self.get_tag::() } @@ -205,34 +213,37 @@ impl<'a> BootInformation<'a> { /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None` /// as it is strictly recommended to get the memory map from the `uefi` /// services. + #[must_use] pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> { // If the EFIBootServicesNotExited is present, then we should not use // the memory map, as it could still be in use. - match self.get_tag::() { - Some(_tag) => { - log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None."); - None - } - None => self.get_tag::(), - } + self.get_tag::().map_or_else( + || self.get_tag::(), |_tag| { + log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None."); + None + }) } /// Search for the EFI 32-bit SDT tag. + #[must_use] pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> { self.get_tag::() } /// Search for the EFI 64-bit SDT tag. + #[must_use] pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> { self.get_tag::() } /// Search for the EFI 32-bit image handle pointer tag. + #[must_use] pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> { self.get_tag::() } /// Search for the EFI 64-bit image handle pointer tag. + #[must_use] pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> { self.get_tag::() } @@ -254,6 +265,7 @@ impl<'a> BootInformation<'a> { /// } /// } /// ``` + #[must_use] pub fn elf_sections(&self) -> Option { let tag = self.get_tag::(); tag.map(|t| { @@ -264,6 +276,7 @@ impl<'a> BootInformation<'a> { /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the /// framebuffer type is unknown, while the framebuffer tag is present. + #[must_use] pub fn framebuffer_tag(&self) -> Option> { self.get_tag::() .map(|tag| match tag.buffer_type() { @@ -273,16 +286,19 @@ impl<'a> BootInformation<'a> { } /// Search for the Image Load Base Physical Address tag. + #[must_use] pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> { self.get_tag::() } /// Search for the Memory map tag. + #[must_use] pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> { self.get_tag::() } /// Get an iterator of all module tags. + #[must_use] pub fn module_tags(&self) -> ModuleIter { module::module_iter(self.tags()) } @@ -293,21 +309,25 @@ impl<'a> BootInformation<'a> { }*/ /// Search for the (ACPI 1.0) RSDP tag. + #[must_use] pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> { self.get_tag::() } /// Search for the (ACPI 2.0 or later) RSDP tag. + #[must_use] pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> { self.get_tag::() } /// Search for the SMBIOS tag. + #[must_use] pub fn smbios_tag(&self) -> Option<&SmbiosTag> { self.get_tag::() } /// Search for the VBE information tag. + #[must_use] pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> { self.get_tag::() } @@ -367,6 +387,7 @@ impl<'a> BootInformation<'a> { /// .unwrap(); /// assert_eq!(tag.name(), Ok("name")); /// ``` + #[must_use] pub fn get_tag(&'a self) -> Option<&'a TagT> { self.tags() .find(|tag| tag.typ == TagT::ID) diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 3cd2ac5c..4e0851c8 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -20,6 +20,7 @@ pub struct BootLoaderNameTag { impl BootLoaderNameTag { #[cfg(feature = "builder")] + #[must_use] pub fn new(name: &str) -> BoxedDst { let mut bytes: Vec<_> = name.bytes().collect(); if !bytes.ends_with(&[0]) { @@ -100,7 +101,7 @@ mod tests { let tag = get_bytes(); let tag = unsafe { &*tag.as_ptr().cast::() }; let tag = tag.cast_tag::(); - assert_eq!({ tag.typ }, TagType::BootLoaderName); + assert_eq!(tag.header.typ, TagType::BootLoaderName); assert_eq!(tag.name().expect("must be valid UTF-8"), MSG); } diff --git a/multiboot2/src/builder/boxed_dst.rs b/multiboot2/src/builder/boxed_dst.rs index bafb8df3..f433104e 100644 --- a/multiboot2/src/builder/boxed_dst.rs +++ b/multiboot2/src/builder/boxed_dst.rs @@ -144,7 +144,7 @@ mod tests { #[test] fn can_hold_tag_trait() { - fn consume(_: &T) {} + const fn consume(_: &T) {} let content = b"hallo\0"; let tag = BoxedDst::::new(content); diff --git a/multiboot2/src/builder/information.rs b/multiboot2/src/builder/information.rs index 8d16a903..91b70eff 100644 --- a/multiboot2/src/builder/information.rs +++ b/multiboot2/src/builder/information.rs @@ -73,6 +73,7 @@ impl Default for InformationBuilder { impl InformationBuilder { /// Creates a new builder. + #[must_use] pub const fn new() -> Self { Self(Vec::new()) } @@ -87,6 +88,7 @@ impl InformationBuilder { /// [`Self::build`]-method is called. This function assumes that the begin /// of the boot information is 8-byte aligned and automatically adds padding /// between tags to ensure that each tag is 8-byte aligned. + #[must_use] pub fn expected_len(&self) -> usize { let tag_size_iter = self.0.iter().map(|(_, bytes)| bytes.len()); @@ -118,6 +120,7 @@ impl InformationBuilder { } /// Constructs the bytes for a valid Multiboot2 information with the given properties. + #[must_use] pub fn build(self) -> BootInformationBytes { const ALIGN: usize = 8; @@ -202,92 +205,109 @@ impl InformationBuilder { } /// Adds a 'basic memory information' tag (represented by [`BasicMemoryInfoTag`]) to the builder. + #[must_use] pub fn basic_memory_info_tag(self, tag: BasicMemoryInfoTag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'bootloader name' tag (represented by [`BootLoaderNameTag`]) to the builder. + #[must_use] pub fn bootloader_name_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'command line' tag (represented by [`CommandLineTag`]) to the builder. + #[must_use] pub fn command_line_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'EFI 32-bit system table pointer' tag (represented by [`EFISdt32Tag`]) to the builder. + #[must_use] pub fn efisdt32_tag(self, tag: EFISdt32Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'EFI 64-bit system table pointer' tag (represented by [`EFISdt64Tag`]) to the builder. + #[must_use] pub fn efisdt64_tag(self, tag: EFISdt64Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'EFI boot services not terminated' tag (represented by [`EFIBootServicesNotExitedTag`]) to the builder. + #[must_use] pub fn efi_boot_services_not_exited_tag(self) -> Self { self.add_tag(&EFIBootServicesNotExitedTag::new()).unwrap() } /// Adds a 'EFI 32-bit image handle pointer' tag (represented by [`EFIImageHandle32Tag`]) to the builder. + #[must_use] pub fn efi_image_handle32(self, tag: EFIImageHandle32Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'EFI 64-bit image handle pointer' tag (represented by [`EFIImageHandle64Tag`]) to the builder. + #[must_use] pub fn efi_image_handle64(self, tag: EFIImageHandle64Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'EFI Memory map' tag (represented by [`EFIMemoryMapTag`]) to the builder. + #[must_use] pub fn efi_memory_map_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'ELF-Symbols' tag (represented by [`ElfSectionsTag`]) to the builder. + #[must_use] pub fn elf_sections_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'Framebuffer info' tag (represented by [`FramebufferTag`]) to the builder. + #[must_use] pub fn framebuffer_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'Image load base physical address' tag (represented by [`ImageLoadPhysAddrTag`]) to the builder. + #[must_use] pub fn image_load_addr(self, tag: ImageLoadPhysAddrTag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a (*none EFI*) 'memory map' tag (represented by [`MemoryMapTag`]) to the builder. + #[must_use] pub fn memory_map_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'Modules' tag (represented by [`ModuleTag`]) to the builder. /// This tag can occur multiple times in boot information. + #[must_use] pub fn add_module_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'ACPI old RSDP' tag (represented by [`RsdpV1Tag`]) to the builder. + #[must_use] pub fn rsdp_v1_tag(self, tag: RsdpV1Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'ACPI new RSDP' tag (represented by [`RsdpV2Tag`]) to the builder. + #[must_use] pub fn rsdp_v2_tag(self, tag: RsdpV2Tag) -> Self { self.add_tag(&tag).unwrap() } /// Adds a 'SMBIOS tables' tag (represented by [`SmbiosTag`]) to the builder. + #[must_use] pub fn smbios_tag(self, tag: BoxedDst) -> Self { self.add_tag(&*tag).unwrap() } - fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool { + const fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool { matches!( tag_type, TagType::Module | TagType::Smbios | TagType::Custom(_) diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 25bd884a..91466ef5 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -8,7 +8,7 @@ use core::str; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -pub(crate) const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); +pub const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); /// This tag contains the command line string. /// @@ -25,6 +25,7 @@ pub struct CommandLineTag { impl CommandLineTag { /// Create a new command line tag from the given string. #[cfg(feature = "builder")] + #[must_use] pub fn new(command_line: &str) -> BoxedDst { let mut bytes: Vec<_> = command_line.bytes().collect(); if !bytes.ends_with(&[0]) { @@ -107,7 +108,7 @@ mod tests { let tag = get_bytes(); let tag = unsafe { &*tag.as_ptr().cast::() }; let tag = tag.cast_tag::(); - assert_eq!({ tag.typ }, TagType::Cmdline); + assert_eq!(tag.header.typ, TagType::Cmdline); assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG); } diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index acc8fbed..9b2e5975 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -20,6 +20,7 @@ pub struct EFISdt32Tag { impl EFISdt32Tag { /// Create a new tag to pass the EFI32 System Table pointer. + #[must_use] pub fn new(pointer: u32) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -28,7 +29,8 @@ impl EFISdt32Tag { } /// The physical address of a i386 EFI system table. - pub fn sdt_address(&self) -> usize { + #[must_use] + pub const fn sdt_address(&self) -> usize { self.pointer as usize } } @@ -49,6 +51,7 @@ pub struct EFISdt64Tag { impl EFISdt64Tag { /// Create a new tag to pass the EFI64 System Table pointer. + #[must_use] pub fn new(pointer: u64) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -57,7 +60,8 @@ impl EFISdt64Tag { } /// The physical address of a x86_64 EFI system table. - pub fn sdt_address(&self) -> usize { + #[must_use] + pub const fn sdt_address(&self) -> usize { self.pointer as usize } } @@ -79,6 +83,7 @@ pub struct EFIImageHandle32Tag { impl EFIImageHandle32Tag { #[cfg(feature = "builder")] + #[must_use] pub fn new(pointer: u32) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -87,7 +92,8 @@ impl EFIImageHandle32Tag { } /// Returns the physical address of the EFI image handle. - pub fn image_handle(&self) -> usize { + #[must_use] + pub const fn image_handle(&self) -> usize { self.pointer as usize } } @@ -109,6 +115,7 @@ pub struct EFIImageHandle64Tag { impl EFIImageHandle64Tag { #[cfg(feature = "builder")] + #[must_use] pub fn new(pointer: u64) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -117,7 +124,8 @@ impl EFIImageHandle64Tag { } /// Returns the physical address of the EFI image handle. - pub fn image_handle(&self) -> usize { + #[must_use] + pub const fn image_handle(&self) -> usize { self.pointer as usize } } @@ -137,6 +145,7 @@ pub struct EFIBootServicesNotExitedTag { impl EFIBootServicesNotExitedTag { #[cfg(feature = "builder")] + #[must_use] pub fn new() -> Self { Self::default() } diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 00579359..eac63e7d 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -26,6 +26,7 @@ pub struct ElfSectionsTag { impl ElfSectionsTag { /// Create a new ElfSectionsTag with the given data. #[cfg(feature = "builder")] + #[must_use] pub fn new( number_of_sections: u32, entry_size: u32, @@ -43,7 +44,7 @@ impl ElfSectionsTag { } /// Get an iterator of loaded ELF sections. - pub(crate) fn sections(&self) -> ElfSectionIter { + pub(crate) const fn sections(&self) -> ElfSectionIter { let string_section_offset = (self.shndx * self.entry_size) as isize; let string_section_ptr = unsafe { self.first_section().offset(string_section_offset) as *const _ }; @@ -55,7 +56,7 @@ impl ElfSectionsTag { } } - fn first_section(&self) -> *const u8 { + const fn first_section(&self) -> *const u8 { &(self.sections[0]) as *const _ } } @@ -174,6 +175,7 @@ struct ElfSectionInner64 { impl ElfSection { /// Get the section type as a `ElfSectionType` enum variant. + #[must_use] pub fn section_type(&self) -> ElfSectionType { match self.get().typ() { 0 => ElfSectionType::Unused, @@ -201,6 +203,7 @@ impl ElfSection { } /// Get the "raw" section type as a `u32` + #[must_use] pub fn section_type_raw(&self) -> u32 { self.get().typ() } @@ -224,6 +227,7 @@ impl ElfSection { } /// Get the physical start address of the section. + #[must_use] pub fn start_address(&self) -> u64 { self.get().addr() } @@ -231,11 +235,13 @@ impl ElfSection { /// Get the physical end address of the section. /// /// This is the same as doing `section.start_address() + section.size()` + #[must_use] pub fn end_address(&self) -> u64 { self.get().addr() + self.get().size() } /// Get the section's size in bytes. + #[must_use] pub fn size(&self) -> u64 { self.get().size() } @@ -246,16 +252,19 @@ impl ElfSection { /// modulo the value of `addrlign`. Currently, only 0 and positive /// integral powers of two are allowed. Values 0 and 1 mean the section has no /// alignment constraints. + #[must_use] pub fn addralign(&self) -> u64 { self.get().addralign() } /// Get the section's flags. + #[must_use] pub fn flags(&self) -> ElfSectionFlags { ElfSectionFlags::from_bits_truncate(self.get().flags()) } /// Check if the `ALLOCATED` flag is set in the section flags. + #[must_use] pub fn is_allocated(&self) -> bool { self.flags().contains(ElfSectionFlags::ALLOCATED) } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 96b1c32b..a5d7b49c 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -17,8 +17,8 @@ struct Reader { } impl Reader { - fn new(ptr: *const T) -> Reader { - Reader { + const fn new(ptr: *const T) -> Self { + Self { ptr: ptr as *const u8, off: 0, } @@ -85,6 +85,7 @@ pub struct FramebufferTag { impl FramebufferTag { #[cfg(feature = "builder")] + #[must_use] pub fn new( address: u64, pitch: u32, @@ -107,27 +108,32 @@ impl FramebufferTag { /// This field is 64-bit wide but bootloader should set it under 4GiB if /// possible for compatibility with payloads which aren’t aware of PAE or /// amd64. - pub fn address(&self) -> u64 { + #[must_use] + pub const fn address(&self) -> u64 { self.address } /// Contains the pitch in bytes. - pub fn pitch(&self) -> u32 { + #[must_use] + pub const fn pitch(&self) -> u32 { self.pitch } /// Contains framebuffer width in pixels. - pub fn width(&self) -> u32 { + #[must_use] + pub const fn width(&self) -> u32 { self.width } /// Contains framebuffer height in pixels. - pub fn height(&self) -> u32 { + #[must_use] + pub const fn height(&self) -> u32 { self.height } /// Contains number of bits per pixel. - pub fn bpp(&self) -> u8 { + #[must_use] + pub const fn bpp(&self) -> u8 { self.bpp } diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index ae2fe995..1f9be8f3 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -17,6 +17,7 @@ pub struct ImageLoadPhysAddrTag { impl ImageLoadPhysAddrTag { #[cfg(feature = "builder")] + #[must_use] pub fn new(load_base_addr: u32) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -25,7 +26,8 @@ impl ImageLoadPhysAddrTag { } /// Returns the load base address. - pub fn load_base_addr(&self) -> u32 { + #[must_use] + pub const fn load_base_addr(&self) -> u32 { self.load_base_addr } } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 436a632c..a2abeadd 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -1,12 +1,20 @@ #![no_std] #![cfg_attr(feature = "unstable", feature(error_in_core))] -#![deny(missing_debug_implementations)] // --- BEGIN STYLE CHECKS --- -// These checks are optional in CI for PRs, as discussed in -// https://github.com/rust-osdev/multiboot2/pull/92 -#![deny(clippy::all)] +#![deny( + clippy::all, + clippy::cargo, + clippy::nursery, + clippy::must_use_candidate, + // clippy::restriction, + // clippy::pedantic +)] +// now allow a few rules which are denied by the above statement +// --> They are either ridiculous, not necessary, or we can't fix them. +#![allow(clippy::multiple_crate_versions)] +// #![deny(missing_docs)] +#![deny(missing_debug_implementations)] #![deny(rustdoc::all)] -#![allow(rustdoc::private_doc_tests)] // --- END STYLE CHECKS --- //! Library that assists parsing the Multiboot2 Information Structure (MBI) from @@ -372,6 +380,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] + #[allow(clippy::cognitive_complexity)] fn vbe_info_tag() { //Taken from GRUB2 running in QEMU. #[repr(C, align(8))] @@ -813,6 +822,7 @@ mod tests { } /// Helper for [`grub2`]. + #[allow(clippy::cognitive_complexity)] fn test_grub2_boot_info( bi: &BootInformation, addr: usize, diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index f7989898..f087dde4 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -36,6 +36,7 @@ pub struct MemoryMapTag { impl MemoryMapTag { #[cfg(feature = "builder")] + #[must_use] pub fn new(areas: &[MemoryArea]) -> BoxedDst { let entry_size: u32 = mem::size_of::().try_into().unwrap(); let entry_version: u32 = 0; @@ -47,12 +48,14 @@ impl MemoryMapTag { } /// Returns the entry size. - pub fn entry_size(&self) -> u32 { + #[must_use] + pub const fn entry_size(&self) -> u32 { self.entry_size } /// Returns the entry version. - pub fn entry_version(&self) -> u32 { + #[must_use] + pub const fn entry_version(&self) -> u32 { self.entry_version } @@ -60,6 +63,7 @@ impl MemoryMapTag { /// /// Usually, this should already reflect the memory consumed by the /// code running this. + #[must_use] pub fn memory_areas(&self) -> &[MemoryArea] { // If this ever fails, we need to model this differently in this crate. assert_eq!(self.entry_size as usize, mem::size_of::()); @@ -100,22 +104,26 @@ impl MemoryArea { } /// The start address of the memory region. - pub fn start_address(&self) -> u64 { + #[must_use] + pub const fn start_address(&self) -> u64 { self.base_addr } /// The end address of the memory region. - pub fn end_address(&self) -> u64 { + #[must_use] + pub const fn end_address(&self) -> u64 { self.base_addr + self.length } /// The size, in bytes, of the memory region. - pub fn size(&self) -> u64 { + #[must_use] + pub const fn size(&self) -> u64 { self.length } /// The type of the memory region. - pub fn typ(&self) -> MemoryAreaTypeId { + #[must_use] + pub const fn typ(&self) -> MemoryAreaTypeId { self.typ } } @@ -215,7 +223,7 @@ impl From for MemoryAreaTypeId { impl PartialEq for MemoryAreaTypeId { fn eq(&self, other: &MemoryAreaType) -> bool { - let val: MemoryAreaTypeId = (*other).into(); + let val: Self = (*other).into(); let val: u32 = val.0; self.0.eq(&val) } @@ -252,6 +260,7 @@ pub struct BasicMemoryInfoTag { } impl BasicMemoryInfoTag { + #[must_use] pub fn new(memory_lower: u32, memory_upper: u32) -> Self { Self { header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), @@ -260,11 +269,13 @@ impl BasicMemoryInfoTag { } } - pub fn memory_lower(&self) -> u32 { + #[must_use] + pub const fn memory_lower(&self) -> u32 { self.memory_lower } - pub fn memory_upper(&self) -> u32 { + #[must_use] + pub const fn memory_upper(&self) -> u32 { self.memory_upper } } @@ -310,6 +321,7 @@ impl EFIMemoryMapTag { /// Create a new EFI memory map tag with the given memory descriptors. /// Version and size can't be set because you're passing a slice of /// EFIMemoryDescs, not the ones you might have gotten from the firmware. + #[must_use] pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> BoxedDst { // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw // release. @@ -337,6 +349,7 @@ impl EFIMemoryMapTag { #[cfg(feature = "builder")] /// Create a new EFI memory map tag from the given EFI memory map. + #[must_use] pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> BoxedDst { assert!(desc_size > 0); assert_eq!(efi_mmap.len() % desc_size as usize, 0); @@ -359,6 +372,7 @@ impl EFIMemoryMapTag { /// /// Usually, this should already reflect the memory consumed by the /// code running this. + #[must_use] pub fn memory_areas(&self) -> EFIMemoryAreaIter { // If this ever fails, this needs to be refactored in a joint-effort // with the uefi-rs project to have all corresponding typings. diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 8359cbc5..fcd43421 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -24,6 +24,7 @@ pub struct ModuleTag { impl ModuleTag { #[cfg(feature = "builder")] + #[must_use] pub fn new(start: u32, end: u32, cmdline: &str) -> BoxedDst { assert!(end > start, "must have a size"); @@ -53,17 +54,20 @@ impl ModuleTag { } /// Start address of the module. - pub fn start_address(&self) -> u32 { + #[must_use] + pub const fn start_address(&self) -> u32 { self.mod_start } /// End address of the module - pub fn end_address(&self) -> u32 { + #[must_use] + pub const fn end_address(&self) -> u32 { self.mod_end } /// The size of the module/the BLOB in memory. - pub fn module_size(&self) -> u32 { + #[must_use] + pub const fn module_size(&self) -> u32 { self.mod_end - self.mod_start } } @@ -91,7 +95,7 @@ impl Debug for ModuleTag { } } -pub fn module_iter(iter: TagIter) -> ModuleIter { +pub const fn module_iter(iter: TagIter) -> ModuleIter { ModuleIter { iter } } @@ -154,7 +158,7 @@ mod tests { let tag = get_bytes(); let tag = unsafe { &*tag.as_ptr().cast::() }; let tag = tag.cast_tag::(); - assert_eq!({ tag.typ }, TagType::Module); + assert_eq!(tag.header.typ, TagType::Module); assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG); } diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index 4d9ca2aa..29ff6ea6 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -36,6 +36,7 @@ pub struct RsdpV1Tag { impl RsdpV1Tag { #[cfg(feature = "builder")] + #[must_use] pub fn new( signature: [u8; 8], checksum: u8, @@ -56,11 +57,12 @@ impl RsdpV1Tag { /// The "RSD PTR " marker signature. /// /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR " - pub fn signature(&self) -> Result<&str, Utf8Error> { + pub const fn signature(&self) -> Result<&str, Utf8Error> { str::from_utf8(&self.signature) } /// Validation of the RSDPv1 checksum + #[must_use] pub fn checksum_is_valid(&self) -> bool { let bytes = unsafe { slice::from_raw_parts(self as *const _ as *const u8, RSDPV1_LENGTH + 8) }; @@ -71,17 +73,19 @@ impl RsdpV1Tag { } /// An OEM-supplied string that identifies the OEM. - pub fn oem_id(&self) -> Result<&str, Utf8Error> { + pub const fn oem_id(&self) -> Result<&str, Utf8Error> { str::from_utf8(&self.oem_id) } /// The revision of the ACPI. - pub fn revision(&self) -> u8 { + #[must_use] + pub const fn revision(&self) -> u8 { self.revision } /// The physical (I repeat: physical) address of the RSDT table. - pub fn rsdt_address(&self) -> usize { + #[must_use] + pub const fn rsdt_address(&self) -> usize { self.rsdt_address as usize } } @@ -112,6 +116,7 @@ pub struct RsdpV2Tag { impl RsdpV2Tag { #[cfg(feature = "builder")] #[allow(clippy::too_many_arguments)] + #[must_use] pub fn new( signature: [u8; 8], checksum: u8, @@ -139,11 +144,12 @@ impl RsdpV2Tag { /// The "RSD PTR " marker signature. /// /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR ". - pub fn signature(&self) -> Result<&str, Utf8Error> { + pub const fn signature(&self) -> Result<&str, Utf8Error> { str::from_utf8(&self.signature) } /// Validation of the RSDPv2 extended checksum + #[must_use] pub fn checksum_is_valid(&self) -> bool { let bytes = unsafe { slice::from_raw_parts(self as *const _ as *const u8, self.length as usize + 8) @@ -155,24 +161,27 @@ impl RsdpV2Tag { } /// An OEM-supplied string that identifies the OEM. - pub fn oem_id(&self) -> Result<&str, Utf8Error> { + pub const fn oem_id(&self) -> Result<&str, Utf8Error> { str::from_utf8(&self.oem_id) } /// The revision of the ACPI. - pub fn revision(&self) -> u8 { + #[must_use] + pub const fn revision(&self) -> u8 { self.revision } /// Physical address of the XSDT table. /// /// On x86, this is truncated from 64-bit to 32-bit. - pub fn xsdt_address(&self) -> usize { + #[must_use] + pub const fn xsdt_address(&self) -> usize { self.xsdt_address as usize } /// This field is used to calculate the checksum of the entire table, including both checksum fields. - pub fn ext_checksum(&self) -> u8 { + #[must_use] + pub const fn ext_checksum(&self) -> u8 { self.ext_checksum } } diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 46f1ad33..f092ed3a 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -23,6 +23,7 @@ pub struct SmbiosTag { impl SmbiosTag { #[cfg(feature = "builder")] + #[must_use] pub fn new(major: u8, minor: u8, tables: &[u8]) -> BoxedDst { let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec(); bytes.extend(tables); diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 8909e6ee..3c249632 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -28,8 +28,8 @@ impl Display for StringError { impl core::error::Error for StringError { fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { match self { - StringError::MissingNul(e) => Some(e), - StringError::Utf8(e) => Some(e), + Self::MissingNul(e) => Some(e), + Self::Utf8(e) => Some(e), } } } @@ -75,11 +75,13 @@ pub struct Tag { impl Tag { /// Returns the underlying type of the tag. + #[must_use] pub fn typ(&self) -> TagType { self.typ.into() } /// Casts the base tag to the specific tag type. + #[must_use] pub fn cast_tag<'a, T: TagTrait + ?Sized + 'a>(&'a self) -> &'a T { assert_eq!(self.typ, T::ID); // Safety: At this point, we trust that "self.size" and the size hint diff --git a/multiboot2/src/tag_trait.rs b/multiboot2/src/tag_trait.rs index 2a073da7..c13b63d1 100644 --- a/multiboot2/src/tag_trait.rs +++ b/multiboot2/src/tag_trait.rs @@ -51,6 +51,7 @@ pub trait TagTrait: Pointee { /// Callers must be sure that the "size" field of the provided [`Tag`] is /// sane and the underlying memory valid. The implementation of this trait /// **must have** a correct [`Self::dst_size`] implementation. + #[must_use] unsafe fn from_base_tag(tag: &Tag) -> &Self { let ptr = core::ptr::addr_of!(*tag); let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag)); diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 49d32d25..9c47c089 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -17,7 +17,8 @@ pub struct TagTypeId(u32); impl TagTypeId { /// Constructor. - pub fn new(val: u32) -> Self { + #[must_use] + pub const fn new(val: u32) -> Self { Self(val) } } @@ -135,6 +136,7 @@ pub enum TagType { impl TagType { /// Convenient wrapper to get the underlying `u32` representation of the tag. + #[must_use] pub fn val(&self) -> u32 { u32::from(*self) } @@ -161,29 +163,29 @@ mod primitive_conversion_impls { impl From for TagType { fn from(value: u32) -> Self { match value { - 0 => TagType::End, - 1 => TagType::Cmdline, - 2 => TagType::BootLoaderName, - 3 => TagType::Module, - 4 => TagType::BasicMeminfo, - 5 => TagType::Bootdev, - 6 => TagType::Mmap, - 7 => TagType::Vbe, - 8 => TagType::Framebuffer, - 9 => TagType::ElfSections, - 10 => TagType::Apm, - 11 => TagType::Efi32, - 12 => TagType::Efi64, - 13 => TagType::Smbios, - 14 => TagType::AcpiV1, - 15 => TagType::AcpiV2, - 16 => TagType::Network, - 17 => TagType::EfiMmap, - 18 => TagType::EfiBs, - 19 => TagType::Efi32Ih, - 20 => TagType::Efi64Ih, - 21 => TagType::LoadBaseAddr, - c => TagType::Custom(c), + 0 => Self::End, + 1 => Self::Cmdline, + 2 => Self::BootLoaderName, + 3 => Self::Module, + 4 => Self::BasicMeminfo, + 5 => Self::Bootdev, + 6 => Self::Mmap, + 7 => Self::Vbe, + 8 => Self::Framebuffer, + 9 => Self::ElfSections, + 10 => Self::Apm, + 11 => Self::Efi32, + 12 => Self::Efi64, + 13 => Self::Smbios, + 14 => Self::AcpiV1, + 15 => Self::AcpiV2, + 16 => Self::Network, + 17 => Self::EfiMmap, + 18 => Self::EfiBs, + 19 => Self::Efi32Ih, + 20 => Self::Efi64Ih, + 21 => Self::LoadBaseAddr, + c => Self::Custom(c), } } } @@ -226,14 +228,14 @@ mod intermediate_conversion_impls { impl From for TagType { fn from(value: TagTypeId) -> Self { let value = u32::from(value); - TagType::from(value) + Self::from(value) } } impl From for TagTypeId { fn from(value: TagType) -> Self { let value = u32::from(value); - TagTypeId::from(value) + Self::from(value) } } } From 776a980570690a8f632941e41beb9689d9a9603e Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 15:25:59 +0200 Subject: [PATCH 13/35] multiboot2: add missing docs --- multiboot2/Changelog.md | 4 +++- multiboot2/src/boot_information.rs | 28 ++++++++++++++++++---------- multiboot2/src/boot_loader_name.rs | 13 +++++++++++++ multiboot2/src/builder/mod.rs | 5 ++++- multiboot2/src/efi.rs | 3 +++ multiboot2/src/end.rs | 4 ++-- multiboot2/src/framebuffer.rs | 1 + multiboot2/src/image_load_addr.rs | 1 + multiboot2/src/lib.rs | 2 +- multiboot2/src/memory_map.rs | 10 +++++++--- multiboot2/src/module.rs | 1 + multiboot2/src/rsdp.rs | 2 ++ multiboot2/src/smbios.rs | 25 ++++++++++++++++++++++--- multiboot2/src/tag.rs | 6 ++++-- 14 files changed, 82 insertions(+), 23 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 6e28ec80..ecb0b7a4 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -3,9 +3,11 @@ ## Unreleased - **Breaking** All functions that returns something useful are now `#[must_use]` +- **Breaking** More public fields in tags were replaced by public getters, such + as `SmbiosTag::major()` - updated dependencies - MSRV is 1.75 -- doc fixes +- documentation enhancements - Introduced new `TagHeader` type as replacement for the `Tag` type that will be changed in the next step. diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index affd4e9d..550842db 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -3,7 +3,7 @@ #[cfg(feature = "builder")] use crate::builder::AsBytes; use crate::framebuffer::UnknownFramebufferType; -use crate::tag::TagIter; +use crate::tag::{TagHeader, TagIter}; use crate::{ module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, @@ -11,6 +11,8 @@ use crate::{ ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag, }; use core::fmt; +use core::mem; +use core::ptr; use derive_more::Display; /// Error type that describes errors while loading/parsing a multiboot2 information structure @@ -39,19 +41,25 @@ impl core::error::Error for MbiLoadError {} #[repr(C)] pub struct BootInformationHeader { // size is multiple of 8 - pub total_size: u32, + total_size: u32, _reserved: u32, // Followed by the boot information tags. } -#[cfg(feature = "builder")] impl BootInformationHeader { + #[cfg(feature = "builder")] pub(crate) const fn new(total_size: u32) -> Self { Self { total_size, _reserved: 0, } } + + /// Returns the total size of the structure. + #[must_use] + pub const fn total_size(&self) -> u32 { + self.total_size + } } #[cfg(feature = "builder")] @@ -70,18 +78,18 @@ impl BootInformationInner { /// Checks if the MBI has a valid end tag by checking the end of the mbi's /// bytes. fn has_valid_end_tag(&self) -> bool { - let end_tag_prototype = EndTag::default(); - - let self_ptr = unsafe { self.tags.as_ptr().sub(size_of::()) }; + let self_ptr = ptr::addr_of!(*self); let end_tag_ptr = unsafe { self_ptr + .cast::() .add(self.header.total_size as usize) - .sub(size_of::()) + .sub(mem::size_of::()) + .cast::() }; - let end_tag = unsafe { &*(end_tag_ptr as *const EndTag) }; + let end_tag = unsafe { &*end_tag_ptr }; - end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size + end_tag.typ == EndTag::ID && end_tag.size as usize == mem::size_of::() } } @@ -127,7 +135,7 @@ impl<'a> BootInformation<'a> { return Err(MbiLoadError::IllegalTotalSize(mbi.total_size)); } - let slice_size = mbi.total_size as usize - size_of::(); + let slice_size = mbi.total_size as usize - mem::size_of::(); // mbi: reference to full mbi let mbi = ptr_meta::from_raw_parts::(ptr.cast(), slice_size); let mbi = &*mbi; diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 4e0851c8..4ce15d5b 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -19,6 +19,7 @@ pub struct BootLoaderNameTag { } impl BootLoaderNameTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(name: &str) -> BoxedDst { @@ -30,6 +31,18 @@ impl BootLoaderNameTag { BoxedDst::new(&bytes) } + /// Returns the underlying [`TagType`]. + #[must_use] + pub fn typ(&self) -> TagType { + self.header.typ.into() + } + + /// Returns the underlying tag size. + #[must_use] + pub const fn size(&self) -> usize { + self.header.size as usize + } + /// Reads the name of the bootloader that is booting the kernel as Rust /// string slice without the null-byte. /// diff --git a/multiboot2/src/builder/mod.rs b/multiboot2/src/builder/mod.rs index 93b68528..7d06c425 100644 --- a/multiboot2/src/builder/mod.rs +++ b/multiboot2/src/builder/mod.rs @@ -8,8 +8,11 @@ pub use boxed_dst::BoxedDst; pub use information::InformationBuilder; /// Helper trait for all structs that need to be serialized that do not -/// implement `TagTrait`. +/// implement [`TagTrait`]. +/// +/// [`TagTrait`]: crate::TagTrait pub trait AsBytes: Sized { + /// Returns the raw bytes of the type. fn as_bytes(&self) -> &[u8] { let ptr = core::ptr::addr_of!(*self); let size = core::mem::size_of::(); diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index 9b2e5975..863b8535 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -82,6 +82,7 @@ pub struct EFIImageHandle32Tag { } impl EFIImageHandle32Tag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(pointer: u32) -> Self { @@ -114,6 +115,7 @@ pub struct EFIImageHandle64Tag { } impl EFIImageHandle64Tag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(pointer: u64) -> Self { @@ -144,6 +146,7 @@ pub struct EFIBootServicesNotExitedTag { } impl EFIBootServicesNotExitedTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new() -> Self { diff --git a/multiboot2/src/end.rs b/multiboot2/src/end.rs index f32351cf..e48b2238 100644 --- a/multiboot2/src/end.rs +++ b/multiboot2/src/end.rs @@ -6,8 +6,8 @@ use crate::{Tag, TagTrait, TagType, TagTypeId}; #[repr(C)] #[derive(Debug)] pub struct EndTag { - pub typ: TagTypeId, - pub size: u32, + typ: TagTypeId, + size: u32, } impl Default for EndTag { diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index a5d7b49c..ae69d844 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -84,6 +84,7 @@ pub struct FramebufferTag { } impl FramebufferTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new( diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index 1f9be8f3..b11127d4 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -16,6 +16,7 @@ pub struct ImageLoadPhysAddrTag { } impl ImageLoadPhysAddrTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(load_base_addr: u32) -> Self { diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index a2abeadd..46a41d5e 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -12,7 +12,7 @@ // now allow a few rules which are denied by the above statement // --> They are either ridiculous, not necessary, or we can't fix them. #![allow(clippy::multiple_crate_versions)] -// #![deny(missing_docs)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rustdoc::all)] // --- END STYLE CHECKS --- diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index f087dde4..b246de56 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -35,6 +35,7 @@ pub struct MemoryMapTag { } impl MemoryMapTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(areas: &[MemoryArea]) -> BoxedDst { @@ -260,21 +261,24 @@ pub struct BasicMemoryInfoTag { } impl BasicMemoryInfoTag { + /// Constructs a new tag. #[must_use] pub fn new(memory_lower: u32, memory_upper: u32) -> Self { Self { - header: TagHeader::new(Self::ID, size_of::().try_into().unwrap()), + header: TagHeader::new(Self::ID, mem::size_of::().try_into().unwrap()), memory_lower, memory_upper, } } #[must_use] + /// Returns the lower memory bound. pub const fn memory_lower(&self) -> u32 { self.memory_lower } #[must_use] + /// Returns the upper memory bound. pub const fn memory_upper(&self) -> u32 { self.memory_upper } @@ -317,10 +321,10 @@ pub struct EFIMemoryMapTag { } impl EFIMemoryMapTag { - #[cfg(feature = "builder")] /// Create a new EFI memory map tag with the given memory descriptors. /// Version and size can't be set because you're passing a slice of /// EFIMemoryDescs, not the ones you might have gotten from the firmware. + #[cfg(feature = "builder")] #[must_use] pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> BoxedDst { // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw @@ -347,8 +351,8 @@ impl EFIMemoryMapTag { ) } - #[cfg(feature = "builder")] /// Create a new EFI memory map tag from the given EFI memory map. + #[cfg(feature = "builder")] #[must_use] pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> BoxedDst { assert!(desc_size > 0); diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index fcd43421..ac8c0a41 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -23,6 +23,7 @@ pub struct ModuleTag { } impl ModuleTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(start: u32, end: u32, cmdline: &str) -> BoxedDst { diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index 29ff6ea6..8a4c289b 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -35,6 +35,7 @@ pub struct RsdpV1Tag { } impl RsdpV1Tag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new( @@ -114,6 +115,7 @@ pub struct RsdpV2Tag { } impl RsdpV2Tag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[allow(clippy::too_many_arguments)] #[must_use] diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index f092ed3a..900d6ba6 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -15,13 +15,14 @@ const METADATA_SIZE: usize = core::mem::size_of::() #[repr(C)] pub struct SmbiosTag { header: TagHeader, - pub major: u8, - pub minor: u8, + major: u8, + minor: u8, _reserved: [u8; 6], - pub tables: [u8], + tables: [u8], } impl SmbiosTag { + /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] pub fn new(major: u8, minor: u8, tables: &[u8]) -> BoxedDst { @@ -29,6 +30,24 @@ impl SmbiosTag { bytes.extend(tables); BoxedDst::new(&bytes) } + + /// Returns the major number. + #[must_use] + pub const fn major(&self) -> u8 { + self.major + } + + /// Returns the major number. + #[must_use] + pub const fn minor(&self) -> u8 { + self.minor + } + + /// Returns the raw tables. + #[must_use] + pub const fn tables(&self) -> &[u8] { + &self.tables + } } impl TagTrait for SmbiosTag { diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 3c249632..7764fc57 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -41,14 +41,15 @@ impl core::error::Error for StringError { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[repr(C)] pub struct TagHeader { + /// The ABI-compatible [`TagType`]. pub typ: TagTypeId, /* u32 */ + /// The total size of the tag including the header. pub size: u32, - // Followed by additional, tag specific fields. + // Followed by optional additional tag specific fields. } impl TagHeader { /// Creates a new header. - #[cfg(feature = "builder")] pub fn new(typ: impl Into, size: u32) -> Self { Self { typ: typ.into(), @@ -67,6 +68,7 @@ impl TagHeader { /// different. #[derive(Clone, Copy)] #[repr(C)] +#[allow(missing_docs)] // type will be removed soon anyway in its form pub struct Tag { pub typ: TagTypeId, // u32 pub size: u32, From a9d93eee6bf95abd3972bf108e3386e23ca0e218 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 15:47:49 +0200 Subject: [PATCH 14/35] multiboot2: streamline code style --- multiboot2/src/boot_loader_name.rs | 4 ++-- multiboot2/src/command_line.rs | 2 +- multiboot2/src/elf_sections.rs | 4 ++-- multiboot2/src/framebuffer.rs | 14 +++++++------- multiboot2/src/module.rs | 4 ++-- multiboot2/src/smbios.rs | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 4ce15d5b..e6817d26 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -3,11 +3,11 @@ use crate::tag::{StringError, TagHeader}; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; -use core::mem::size_of; +use core::mem; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -const METADATA_SIZE: usize = size_of::() + size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); /// The bootloader name tag. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 91466ef5..b48eeddb 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -8,7 +8,7 @@ use core::str; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -pub const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); /// This tag contains the command line string. /// diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index eac63e7d..849b08e5 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -4,10 +4,10 @@ use crate::builder::BoxedDst; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; -use core::mem::size_of; +use core::mem; use core::str::Utf8Error; -const METADATA_SIZE: usize = size_of::() + 4 * size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + 4 * mem::size_of::(); /// This tag contains the section header table from an ELF binary. // The sections iterator is provided via the [`ElfSectionsTag::sections`] diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index ae69d844..50167b88 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -3,7 +3,7 @@ use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::Debug; -use core::mem::size_of; +use core::mem; use core::slice; use derive_more::Display; #[cfg(feature = "builder")] @@ -42,11 +42,11 @@ impl Reader { } } -const METADATA_SIZE: usize = size_of::() - + 4 * size_of::() - + size_of::() - + size_of::() - + 2 * size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + + 4 * mem::size_of::() + + mem::size_of::() + + mem::size_of::() + + 2 * mem::size_of::(); /// The VBE Framebuffer information tag. #[derive(ptr_meta::Pointee, Eq)] @@ -343,6 +343,6 @@ mod tests { // Compile time test #[test] fn test_size() { - assert_eq!(size_of::(), 3) + assert_eq!(mem::size_of::(), 3) } } diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index ac8c0a41..5dd65047 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -3,11 +3,11 @@ use crate::tag::{StringError, TagHeader, TagIter}; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; -use core::mem::size_of; +use core::mem; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -const METADATA_SIZE: usize = size_of::() + 3 * size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); /// The module tag can occur multiple times and specifies passed boot modules /// (blobs in memory). The tag itself doesn't include the blog, but references diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 900d6ba6..e66b732e 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -5,10 +5,10 @@ use crate::builder::BoxedDst; use crate::tag::TagHeader; use crate::{Tag, TagTrait, TagType, TagTypeId}; use core::fmt::Debug; +use core::mem; -const METADATA_SIZE: usize = core::mem::size_of::() - + core::mem::size_of::() - + core::mem::size_of::() * 8; +const METADATA_SIZE: usize = + mem::size_of::() + mem::size_of::() + mem::size_of::() * 8; /// This tag contains a copy of SMBIOS tables as well as their version. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] From b2966736b78a61465125d39187b6b63e7fc49323 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 16:12:36 +0200 Subject: [PATCH 15/35] multiboot2-header: improve code style --- multiboot2-header/Changelog.md | 3 +- multiboot2-header/src/address.rs | 41 +++++++--- multiboot2-header/src/builder/header.rs | 40 +++++++++- .../src/builder/information_request.rs | 4 + multiboot2-header/src/builder/traits.rs | 2 +- multiboot2-header/src/console.rs | 33 +++++--- multiboot2-header/src/end.rs | 35 +++++---- multiboot2-header/src/entry_address.rs | 40 ++++++---- multiboot2-header/src/entry_efi_32.rs | 41 ++++++---- multiboot2-header/src/entry_efi_64.rs | 41 ++++++---- multiboot2-header/src/framebuffer.rs | 46 ++++++++---- multiboot2-header/src/header.rs | 75 +++++++++++++------ multiboot2-header/src/information_request.rs | 46 +++++++----- multiboot2-header/src/lib.rs | 18 ++++- multiboot2-header/src/module_align.rs | 30 +++++--- multiboot2-header/src/relocatable.rs | 48 ++++++++---- multiboot2-header/src/tags.rs | 40 +++++++--- multiboot2-header/src/uefi_bs.rs | 29 ++++--- 18 files changed, 419 insertions(+), 193 deletions(-) diff --git a/multiboot2-header/Changelog.md b/multiboot2-header/Changelog.md index 55986349..df9f05e8 100644 --- a/multiboot2-header/Changelog.md +++ b/multiboot2-header/Changelog.md @@ -2,8 +2,9 @@ ## Unreleased +- **Breaking** All functions that returns something useful are now `#[must_use]` - updated dependencies -- MSRV is 1.75 +- documentation enhancements ## 0.4.0 (2024-05-01) diff --git a/multiboot2-header/src/address.rs b/multiboot2-header/src/address.rs index e15e5035..7a450e9e 100644 --- a/multiboot2-header/src/address.rs +++ b/multiboot2-header/src/address.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::mem::size_of; /// This information does not need to be provided if the kernel image is in ELF @@ -8,9 +8,7 @@ use core::mem::size_of; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct AddressHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, /// Contains the address corresponding to the beginning of the Multiboot2 header — the physical memory location at which the magic value is supposed to be loaded. This field serves to synchronize the mapping between OS image offsets and physical memory addresses. header_addr: u32, /// Contains the physical address of the beginning of the text segment. The offset in the OS image file at which to start loading is defined by the offset at which the header was found, minus (header_addr - load_addr). load_addr must be less than or equal to header_addr. @@ -24,6 +22,8 @@ pub struct AddressHeaderTag { } impl AddressHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new( flags: HeaderTagFlag, header_addr: u32, @@ -31,10 +31,9 @@ impl AddressHeaderTag { load_end_addr: u32, bss_end_addr: u32, ) -> Self { - AddressHeaderTag { - typ: HeaderTagType::Address, - flags, - size: size_of::() as u32, + let header = HeaderTagHeader::new(HeaderTagType::Address, flags, size_of::() as u32); + Self { + header, header_addr, load_addr, load_end_addr, @@ -42,24 +41,44 @@ impl AddressHeaderTag { } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the header address. + #[must_use] pub const fn header_addr(&self) -> u32 { self.header_addr } + + /// Returns the load begin address. + #[must_use] pub const fn load_addr(&self) -> u32 { self.load_addr } + + /// Returns the load end address. + #[must_use] pub const fn load_end_addr(&self) -> u32 { self.load_end_addr } + + /// Returns the bss end address. + #[must_use] pub const fn bss_end_addr(&self) -> u32 { self.bss_end_addr } diff --git a/multiboot2-header/src/builder/header.rs b/multiboot2-header/src/builder/header.rs index 0ab65d28..ff6ba7bb 100644 --- a/multiboot2-header/src/builder/header.rs +++ b/multiboot2-header/src/builder/header.rs @@ -72,6 +72,8 @@ pub struct HeaderBuilder { } impl HeaderBuilder { + /// Creates a new builder. + #[must_use] pub const fn new(arch: HeaderTagISA) -> Self { Self { arch, @@ -98,6 +100,7 @@ impl HeaderBuilder { /// Returns the expected length of the Multiboot2 header, when the /// [`Self::build`]-method gets called. + #[must_use] pub fn expected_len(&self) -> usize { let base_len = size_of::(); // size_or_up_aligned not required, because basic header length is 16 and the @@ -159,7 +162,8 @@ impl HeaderBuilder { } /// Constructs the bytes for a valid Multiboot2 header with the given properties. - pub fn build(mut self) -> HeaderBytes { + #[must_use] + pub fn build(self) -> HeaderBytes { const ALIGN: usize = 8; // PHASE 1/2: Prepare Vector @@ -205,7 +209,7 @@ impl HeaderBuilder { } /// Helper method that adds all the tags to the given vector. - fn build_add_tags(&mut self, bytes: &mut Vec) { + fn build_add_tags(&self, bytes: &mut Vec) { Self::build_add_bytes( bytes, // important that we write the correct expected length into the header! @@ -247,7 +251,10 @@ impl HeaderBuilder { } // clippy thinks this can be a const fn but the compiler denies it - #[allow(clippy::missing_const_for_fn)] + // #[allow(clippy::missing_const_for_fn)] + /// Adds information requests from the + /// [`InformationRequestHeaderTagBuilder`] to the builder. + #[must_use] pub fn information_request_tag( mut self, information_request_tag: InformationRequestHeaderTagBuilder, @@ -255,38 +262,65 @@ impl HeaderBuilder { self.information_request_tag = Some(information_request_tag); self } + + /// Adds a [`AddressHeaderTag`] to the builder. + #[must_use] pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self { self.address_tag = Some(address_tag); self } + + /// Adds a [`EntryAddressHeaderTag`] to the builder. + #[must_use] pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self { self.entry_tag = Some(entry_tag); self } + + /// Adds a [`ConsoleHeaderTag`] to the builder. + #[must_use] pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self { self.console_tag = Some(console_tag); self } + + /// Adds a [`FramebufferHeaderTag`] to the builder. + #[must_use] pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self { self.framebuffer_tag = Some(framebuffer_tag); self } + + /// Adds a [`ModuleAlignHeaderTag`] to the builder. + #[must_use] pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self { self.module_align_tag = Some(module_align_tag); self } + + /// Adds a [`EfiBootServiceHeaderTag`] to the builder. + #[must_use] pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self { self.efi_bs_tag = Some(efi_bs_tag); self } + + /// Adds a [`EntryEfi32HeaderTag`] to the builder. + #[must_use] pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self { self.efi_32_tag = Some(efi_32_tag); self } + + /// Adds a [`EntryEfi64HeaderTag`] to the builder. + #[must_use] pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self { self.efi_64_tag = Some(efi_64_tag); self } + + /// Adds a [`RelocatableHeaderTag`] to the builder. + #[must_use] pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self { self.relocatable_tag = Some(relocatable_tag); self diff --git a/multiboot2-header/src/builder/information_request.rs b/multiboot2-header/src/builder/information_request.rs index 543e7cc9..f5448541 100644 --- a/multiboot2-header/src/builder/information_request.rs +++ b/multiboot2-header/src/builder/information_request.rs @@ -22,6 +22,7 @@ pub struct InformationRequestHeaderTagBuilder { #[cfg(feature = "builder")] impl InformationRequestHeaderTagBuilder { /// New builder. + #[must_use] pub const fn new(flag: HeaderTagFlag) -> Self { Self { irs: BTreeSet::new(), @@ -31,6 +32,7 @@ impl InformationRequestHeaderTagBuilder { /// Returns the expected length of the information request tag, /// when the `build`-method gets called. + #[must_use] pub fn expected_len(&self) -> usize { let basic_header_size = size_of::>(); let req_tags_size = self.irs.len() * size_of::(); @@ -38,12 +40,14 @@ impl InformationRequestHeaderTagBuilder { } /// Adds an [`MbiTagType`] to the information request. + #[must_use] pub fn add_ir(mut self, tag: MbiTagType) -> Self { self.irs.insert(tag); self } /// Adds multiple [`MbiTagType`] to the information request. + #[must_use] pub fn add_irs(mut self, tags: &[MbiTagType]) -> Self { self.irs.extend(tags); self diff --git a/multiboot2-header/src/builder/traits.rs b/multiboot2-header/src/builder/traits.rs index f936c4e8..cea09fdf 100644 --- a/multiboot2-header/src/builder/traits.rs +++ b/multiboot2-header/src/builder/traits.rs @@ -11,7 +11,7 @@ use core::mem::size_of; /// Trait for all tags that helps to create a byte array from the tag. /// Useful in builders to construct a byte vector that /// represents the Multiboot2 header with all its tags. -pub(crate) trait StructAsBytes: Sized { +pub trait StructAsBytes: Sized { /// Returns the size in bytes of the struct, as known during compile /// time. This doesn't use read the "size" field of tags. fn byte_size(&self) -> usize { diff --git a/multiboot2-header/src/console.rs b/multiboot2-header/src/console.rs index 6307201d..392a6c07 100644 --- a/multiboot2-header/src/console.rs +++ b/multiboot2-header/src/console.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::mem::size_of; /// Possible flags for [`ConsoleHeaderTag`]. @@ -16,31 +16,42 @@ pub enum ConsoleHeaderTagFlags { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct ConsoleHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, console_flags: ConsoleHeaderTagFlags, } impl ConsoleHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self { - ConsoleHeaderTag { - typ: HeaderTagType::ConsoleFlags, - flags, - size: size_of::() as u32, + let header = + HeaderTagHeader::new(HeaderTagType::ConsoleFlags, flags, size_of::() as u32); + Self { + header, console_flags, } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the [`ConsoleHeaderTagFlags`]. + #[must_use] pub const fn console_flags(&self) -> ConsoleHeaderTagFlags { self.console_flags } diff --git a/multiboot2-header/src/end.rs b/multiboot2-header/src/end.rs index 15f8c76f..1e806460 100644 --- a/multiboot2-header/src/end.rs +++ b/multiboot2-header/src/end.rs @@ -1,15 +1,11 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::mem::size_of; /// Terminates a list of optional tags in a Multiboot2 header. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EndHeaderTag { - // u16 value - typ: HeaderTagType, - // u16 value - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, } impl Default for EndHeaderTag { @@ -19,22 +15,33 @@ impl Default for EndHeaderTag { } impl EndHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new() -> Self { - EndHeaderTag { - typ: HeaderTagType::End, - flags: HeaderTagFlag::Required, - size: size_of::() as u32, - } + let header = HeaderTagHeader::new( + HeaderTagType::EntryAddress, + HeaderTagFlag::Required, + size_of::() as u32, + ); + Self { header } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } } diff --git a/multiboot2-header/src/entry_address.rs b/multiboot2-header/src/entry_address.rs index 513e7b35..cef429e3 100644 --- a/multiboot2-header/src/entry_address.rs +++ b/multiboot2-header/src/entry_address.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::fmt; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -8,31 +8,39 @@ use core::mem::size_of; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EntryAddressHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, entry_addr: u32, } impl EntryAddressHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self { - EntryAddressHeaderTag { - typ: HeaderTagType::EntryAddress, - flags, - size: size_of::() as u32, - entry_addr, - } + let header = + HeaderTagHeader::new(HeaderTagType::EntryAddress, flags, size_of::() as u32); + Self { header, entry_addr } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the entry address. + #[must_use] pub const fn entry_addr(&self) -> u32 { self.entry_addr } @@ -41,9 +49,9 @@ impl EntryAddressHeaderTag { impl Debug for EntryAddressHeaderTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("EntryAddressHeaderTag") - .field("type", &{ self.typ }) - .field("flags", &{ self.flags }) - .field("size", &{ self.size }) + .field("type", &self.typ()) + .field("flags", &self.flags()) + .field("size", &self.size()) .field("entry_addr", &(self.entry_addr as *const u32)) .finish() } diff --git a/multiboot2-header/src/entry_efi_32.rs b/multiboot2-header/src/entry_efi_32.rs index 5f6818c1..c6ca3a71 100644 --- a/multiboot2-header/src/entry_efi_32.rs +++ b/multiboot2-header/src/entry_efi_32.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::fmt; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -12,31 +12,42 @@ use core::mem::size_of; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EntryEfi32HeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, entry_addr: u32, } impl EntryEfi32HeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self { - EntryEfi32HeaderTag { - typ: HeaderTagType::EntryAddressEFI32, + let header = HeaderTagHeader::new( + HeaderTagType::EntryAddressEFI32, flags, - size: size_of::() as u32, - entry_addr, - } + size_of::() as u32, + ); + Self { header, entry_addr } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the entry address. + #[must_use] pub const fn entry_addr(&self) -> u32 { self.entry_addr } @@ -45,9 +56,9 @@ impl EntryEfi32HeaderTag { impl Debug for EntryEfi32HeaderTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("EntryEfi32HeaderTag") - .field("type", &{ self.typ }) - .field("flags", &{ self.flags }) - .field("size", &{ self.size }) + .field("type", &self.typ()) + .field("flags", &self.flags()) + .field("size", &self.size()) .field("entry_addr", &(self.entry_addr as *const u32)) .finish() } diff --git a/multiboot2-header/src/entry_efi_64.rs b/multiboot2-header/src/entry_efi_64.rs index dea41a95..e145f5cf 100644 --- a/multiboot2-header/src/entry_efi_64.rs +++ b/multiboot2-header/src/entry_efi_64.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::fmt; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -12,31 +12,42 @@ use core::mem::size_of; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EntryEfi64HeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, entry_addr: u32, } impl EntryEfi64HeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self { - EntryEfi64HeaderTag { - typ: HeaderTagType::EntryAddressEFI64, + let header = HeaderTagHeader::new( + HeaderTagType::EntryAddressEFI64, flags, - size: size_of::() as u32, - entry_addr, - } + size_of::() as u32, + ); + Self { header, entry_addr } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the entry address. + #[must_use] pub const fn entry_addr(&self) -> u32 { self.entry_addr } @@ -45,9 +56,9 @@ impl EntryEfi64HeaderTag { impl Debug for EntryEfi64HeaderTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("EntryEfi64HeaderTag") - .field("type", &{ self.typ }) - .field("flags", &{ self.flags }) - .field("size", &{ self.size }) + .field("type", &self.typ()) + .field("flags", &self.flags()) + .field("size", &self.size()) .field("entry_addr", &(self.entry_addr as *const u32)) .finish() } diff --git a/multiboot2-header/src/framebuffer.rs b/multiboot2-header/src/framebuffer.rs index 1b9d1772..dfddb2d8 100644 --- a/multiboot2-header/src/framebuffer.rs +++ b/multiboot2-header/src/framebuffer.rs @@ -1,5 +1,5 @@ -use crate::{HeaderTagFlag, HeaderTagType}; -use core::mem::size_of; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; +use core::mem; /// Specifies the preferred graphics mode. If this tag /// is present the bootloader assumes that the payload @@ -8,41 +8,61 @@ use core::mem::size_of; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct FramebufferHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, width: u32, height: u32, depth: u32, } impl FramebufferHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag, width: u32, height: u32, depth: u32) -> Self { - FramebufferHeaderTag { - typ: HeaderTagType::Framebuffer, + let header = HeaderTagHeader::new( + HeaderTagType::Framebuffer, flags, - size: size_of::() as u32, + mem::size_of::() as u32, + ); + Self { + header, width, height, depth, } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Returns the width. + #[must_use] pub const fn width(&self) -> u32 { self.width } + + /// Returns the height. + #[must_use] pub const fn height(&self) -> u32 { self.height } + + /// Returns the depth. + #[must_use] pub const fn depth(&self) -> u32 { self.depth } @@ -50,12 +70,12 @@ impl FramebufferHeaderTag { #[cfg(test)] mod tests { - use crate::FramebufferHeaderTag; + use super::*; #[test] fn test_assert_size() { assert_eq!( - core::mem::size_of::(), + mem::size_of::(), 2 + 2 + 4 + 4 + 4 + 4 ); } diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index cb256f92..4c3c988b 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -1,8 +1,8 @@ use crate::{ AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag, EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, - HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag, - RelocatableHeaderTag, + HeaderTagHeader, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, + ModuleAlignHeaderTag, RelocatableHeaderTag, }; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -103,89 +103,106 @@ impl<'a> Multiboot2Header<'a> { } /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`]. + #[must_use] pub const fn verify_checksum(&self) -> bool { self.0.verify_checksum() } /// Wrapper around [`Multiboot2BasicHeader::header_magic`]. + #[must_use] pub const fn header_magic(&self) -> u32 { self.0.header_magic() } /// Wrapper around [`Multiboot2BasicHeader::arch`]. + #[must_use] pub const fn arch(&self) -> HeaderTagISA { self.0.arch() } /// Wrapper around [`Multiboot2BasicHeader::length`]. + #[must_use] pub const fn length(&self) -> u32 { self.0.length() } /// Wrapper around [`Multiboot2BasicHeader::checksum`]. + #[must_use] pub const fn checksum(&self) -> u32 { self.0.checksum() } /// Wrapper around [`Multiboot2BasicHeader::tag_iter`]. + #[must_use] pub fn iter(&self) -> Multiboot2HeaderTagIter { self.0.tag_iter() } /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`]. + #[must_use] pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 { Multiboot2BasicHeader::calc_checksum(magic, arch, length) } /// Search for the address header tag. + #[must_use] pub fn address_tag(&self) -> Option<&AddressHeaderTag> { self.get_tag(HeaderTagType::Address) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const AddressHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const AddressHeaderTag) }) } /// Search for the entry address header tag. + #[must_use] pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> { self.get_tag(HeaderTagType::EntryAddress) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryAddressHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryAddressHeaderTag) }) } /// Search for the EFI32 entry address header tag. + #[must_use] pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> { self.get_tag(HeaderTagType::EntryAddressEFI32) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi32HeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi32HeaderTag) }) } /// Search for the EFI64 entry address header tag. + #[must_use] pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> { self.get_tag(HeaderTagType::EntryAddressEFI64) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi64HeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi64HeaderTag) }) } /// Search for the console flags header tag. + #[must_use] pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> { self.get_tag(HeaderTagType::ConsoleFlags) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ConsoleHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ConsoleHeaderTag) }) } /// Search for the framebuffer header tag. + #[must_use] pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> { self.get_tag(HeaderTagType::Framebuffer) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const FramebufferHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const FramebufferHeaderTag) }) } /// Search for the module align header tag. + #[must_use] pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> { self.get_tag(HeaderTagType::ModuleAlign) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ModuleAlignHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ModuleAlignHeaderTag) }) } /// Search for the EFI Boot Services header tag. + #[must_use] pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> { - self.get_tag(HeaderTagType::EfiBS) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EfiBootServiceHeaderTag) }) + self.get_tag(HeaderTagType::EfiBS).map(|tag| unsafe { + &*(tag as *const HeaderTagHeader as *const EfiBootServiceHeaderTag) + }) } /// Search for the EFI32 entry address header tag. + #[must_use] pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> { self.get_tag(HeaderTagType::Relocatable) - .map(|tag| unsafe { &*(tag as *const HeaderTag as *const RelocatableHeaderTag) }) + .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const RelocatableHeaderTag) }) } - fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTag> { + fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTagHeader> { self.iter() .map(|tag| unsafe { tag.as_ref() }.unwrap()) .find(|tag| tag.typ() == typ) @@ -229,7 +246,7 @@ impl Multiboot2BasicHeader { pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self { let magic = MAGIC; let checksum = Self::calc_checksum(magic, arch, length); - Multiboot2BasicHeader { + Self { header_magic: magic, arch, length, @@ -238,25 +255,38 @@ impl Multiboot2BasicHeader { } /// Verifies that a Multiboot2 header is valid. + #[must_use] pub const fn verify_checksum(&self) -> bool { let check = Self::calc_checksum(self.header_magic, self.arch, self.length); check == self.checksum } /// Calculates the checksum as described in the spec. + #[must_use] pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 { (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32 } + /// Returns the header magic. + #[must_use] pub const fn header_magic(&self) -> u32 { self.header_magic } + + /// Returns the [`HeaderTagISA`]. + #[must_use] pub const fn arch(&self) -> HeaderTagISA { self.arch } + + /// Returns the length. + #[must_use] pub const fn length(&self) -> u32 { self.length } + + /// Returns the checksum. + #[must_use] pub const fn checksum(&self) -> u32 { self.checksum } @@ -265,12 +295,13 @@ impl Multiboot2BasicHeader { /// /// # Panics /// See doc of [`Multiboot2HeaderTagIter`]. + #[must_use] pub fn tag_iter(&self) -> Multiboot2HeaderTagIter { - let base_hdr_size = size_of::(); + let base_hdr_size = size_of::(); if base_hdr_size == self.length as usize { panic!("No end tag!"); } - let tag_base_addr = self as *const Multiboot2BasicHeader; + let tag_base_addr = self as *const Self; // cast to u8 so that the offset in bytes works correctly let tag_base_addr = tag_base_addr as *const u8; // tag_base_addr should now point behind the "static" members @@ -278,7 +309,7 @@ impl Multiboot2BasicHeader { // align pointer to 8 byte according to spec let tag_base_addr = unsafe { tag_base_addr.add(tag_base_addr.align_offset(8)) }; // cast back - let tag_base_addr = tag_base_addr as *const HeaderTag; + let tag_base_addr = tag_base_addr as *const HeaderTagHeader; let tags_len = self.length as usize - base_hdr_size; Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32) } @@ -307,7 +338,7 @@ impl Debug for Multiboot2BasicHeader { #[derive(Clone)] pub struct Multiboot2HeaderTagIter { /// 8-byte aligned base address - base: *const HeaderTag, + base: *const HeaderTagHeader, /// Offset in bytes from the base address. /// Always <= than size. n: u32, @@ -324,11 +355,11 @@ pub struct Multiboot2HeaderTagIter { } impl Multiboot2HeaderTagIter { - fn new(base: *const HeaderTag, size: u32) -> Self { + fn new(base: *const HeaderTagHeader, size: u32) -> Self { // transform to byte pointer => offset works properly let base = base as *const u8; let base = unsafe { base.add(base.align_offset(8)) }; - let base = base as *const HeaderTag; + let base = base as *const HeaderTagHeader; Self { base, n: 0, @@ -340,7 +371,7 @@ impl Multiboot2HeaderTagIter { } impl Iterator for Multiboot2HeaderTagIter { - type Item = *const HeaderTag; + type Item = *const HeaderTagHeader; fn next(&mut self) -> Option { // no more bytes left to check; length reached @@ -351,7 +382,7 @@ impl Iterator for Multiboot2HeaderTagIter { // transform to byte ptr => offset works correctly let ptr = self.base as *const u8; let ptr = unsafe { ptr.add(self.n as usize) }; - let ptr = ptr as *const HeaderTag; + let ptr = ptr as *const HeaderTagHeader; assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned"); let tag = unsafe { &*ptr }; assert!( diff --git a/multiboot2-header/src/information_request.rs b/multiboot2-header/src/information_request.rs index c5bb1d6c..f19e15bc 100644 --- a/multiboot2-header/src/information_request.rs +++ b/multiboot2-header/src/information_request.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, MbiTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, MbiTagType}; use crate::{HeaderTagType, MbiTagTypeId}; use core::fmt; use core::fmt::{Debug, Formatter}; @@ -11,9 +11,7 @@ use multiboot2::TagType; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct InformationRequestHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, // Length is determined by size. // Must be parsed during runtime with unsafe pointer magic and the size field. requests: [MbiTagTypeId; N], @@ -23,28 +21,38 @@ impl InformationRequestHeaderTag { /// Creates a new object. The size parameter is the value of the size property. /// It doesn't have to match with `N` necessarily, because during compile time we /// can't know the size of the tag in all runtime situations. + #[must_use] pub fn new(flags: HeaderTagFlag, requests: [MbiTagTypeId; N], size: Option) -> Self { - InformationRequestHeaderTag { - typ: HeaderTagType::InformationRequest, + let header = HeaderTagHeader::new( + HeaderTagType::InformationRequest, flags, - size: size.unwrap_or(size_of::() as u32), - requests, - } + size.unwrap_or(size_of::() as u32), + ); + Self { header, requests } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } /// Returns the requests as array. Only works if the number of requests /// is known at compile time. For safety and correctness during runtime, /// you should use `req_iter()`. + #[must_use] pub const fn requests(&self) -> [MbiTagTypeId; N] { // cheap to copy, otherwise difficult with lifetime self.requests @@ -54,9 +62,10 @@ impl InformationRequestHeaderTag { /// from the `size`-property. This method is useful /// because this struct uses a const generic, but during runtime /// we don't know the value in almost any case. + #[must_use] pub const fn dynamic_requests_size(&self) -> u32 { let base_struct_size = size_of::>(); - let size_diff = self.size - base_struct_size as u32; + let size_diff = self.size() - base_struct_size as u32; if size_diff > 0 { size_diff / size_of::() as u32 } else { @@ -65,10 +74,11 @@ impl InformationRequestHeaderTag { } /// Returns an [`InformationRequestHeaderTagIter`]. + #[must_use] pub const fn req_iter(&self) -> InformationRequestHeaderTagIter { let base_struct_size = size_of::>(); let count = self.dynamic_requests_size(); - let base_ptr = self as *const InformationRequestHeaderTag; + let base_ptr = self as *const Self; let base_ptr = base_ptr as *const u8; let base_ptr = unsafe { base_ptr.add(base_struct_size) }; let base_ptr = base_ptr as *const MbiTagTypeId; @@ -79,10 +89,10 @@ impl InformationRequestHeaderTag { impl Debug for InformationRequestHeaderTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("InformationRequestHeaderTag") - .field("type", &{ self.typ }) - .field("flags", &{ self.flags }) - .field("size", &{ self.size }) - .field("requests", &{ self.req_iter() }) + .field("type", &self.typ()) + .field("flags", &self.flags()) + .field("size", &self.size()) + .field("requests", &self.req_iter()) .finish() } } diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 165bace8..578511e8 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -38,10 +38,22 @@ #![no_std] #![cfg_attr(feature = "unstable", feature(error_in_core))] -#![deny(rustdoc::all)] -#![deny(clippy::all)] -#![deny(clippy::missing_const_for_fn)] +// --- BEGIN STYLE CHECKS --- +#![deny( + clippy::all, + clippy::cargo, + clippy::nursery, + clippy::must_use_candidate, + // clippy::restriction, + // clippy::pedantic +)] +// now allow a few rules which are denied by the above statement +// --> They are either ridiculous, not necessary, or we can't fix them. +#![allow(clippy::multiple_crate_versions)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] +#![deny(rustdoc::all)] +// --- END STYLE CHECKS --- #[cfg(feature = "builder")] extern crate alloc; diff --git a/multiboot2-header/src/module_align.rs b/multiboot2-header/src/module_align.rs index 0ec2eb4b..c67acc58 100644 --- a/multiboot2-header/src/module_align.rs +++ b/multiboot2-header/src/module_align.rs @@ -1,32 +1,38 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::mem::size_of; /// If this tag is present, provided boot modules must be page aligned. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct ModuleAlignHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, } impl ModuleAlignHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag) -> Self { - ModuleAlignHeaderTag { - typ: HeaderTagType::ModuleAlign, - flags, - size: size_of::() as u32, - } + let header = + HeaderTagHeader::new(HeaderTagType::ModuleAlign, flags, size_of::() as u32); + Self { header } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } } diff --git a/multiboot2-header/src/relocatable.rs b/multiboot2-header/src/relocatable.rs index ca8aea49..80cfaf96 100644 --- a/multiboot2-header/src/relocatable.rs +++ b/multiboot2-header/src/relocatable.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::fmt; use core::fmt::{Debug, Formatter}; use core::mem::size_of; @@ -22,9 +22,7 @@ pub enum RelocatableHeaderTagPreference { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct RelocatableHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, /// Lowest possible physical address at which image should be loaded. The bootloader cannot load any part of image below this address min_addr: u32, /// Highest possible physical address at which loaded image should end. The bootloader cannot load any part of image above this address. @@ -35,6 +33,8 @@ pub struct RelocatableHeaderTag { } impl RelocatableHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new( flags: HeaderTagFlag, min_addr: u32, @@ -42,10 +42,10 @@ impl RelocatableHeaderTag { align: u32, preference: RelocatableHeaderTagPreference, ) -> Self { - RelocatableHeaderTag { - typ: HeaderTagType::Relocatable, - flags, - size: size_of::() as u32, + let header = + HeaderTagHeader::new(HeaderTagType::Relocatable, flags, size_of::() as u32); + Self { + header, min_addr, max_addr, align, @@ -53,24 +53,44 @@ impl RelocatableHeaderTag { } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } + + /// Return the minimum address. + #[must_use] pub const fn min_addr(&self) -> u32 { self.min_addr } + + /// Return the maximum address. + #[must_use] pub const fn max_addr(&self) -> u32 { self.max_addr } + + /// Return the alignment. + #[must_use] pub const fn align(&self) -> u32 { self.align } + + /// Return the preference. + #[must_use] pub const fn preference(&self) -> RelocatableHeaderTagPreference { self.preference } @@ -79,9 +99,9 @@ impl RelocatableHeaderTag { impl Debug for RelocatableHeaderTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("RelocatableHeaderTag") - .field("type", &{ self.typ }) - .field("flags", &{ self.flags }) - .field("size", &{ self.size }) + .field("type", &self.typ()) + .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)) diff --git a/multiboot2-header/src/tags.rs b/multiboot2-header/src/tags.rs index 51242697..c51aedc0 100644 --- a/multiboot2-header/src/tags.rs +++ b/multiboot2-header/src/tags.rs @@ -17,7 +17,7 @@ pub enum HeaderTagISA { /// Possible types for header tags of a Multiboot2 header. The names and values are taken /// from the example C code at the bottom of the Multiboot2 specification. This value -/// stands in the `typ` property of [`crate::tags::HeaderTag`]. +/// stands in the `typ` property of [`HeaderTagHeader`]. #[repr(u16)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum HeaderTagType { @@ -47,6 +47,7 @@ pub enum HeaderTagType { impl HeaderTagType { /// Returns the number of possible variants. + #[must_use] pub const fn count() -> u32 { 11 } @@ -56,31 +57,46 @@ impl HeaderTagType { #[repr(u16)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum HeaderTagFlag { + /// Bootloader must provide this tag. If this is not possible, the + /// bootloader will fail loading the kernel. Required = 0, + /// Bootloader should provide the tag if possible. Optional = 1, } -/// Common properties for all header tags. Other tags may have additional fields -/// that depend on the `typ` and the `size` field. All tags share the same beginning of the -/// struct. +/// The common header that all header tags share. Specific tags may have +/// additional fields that depend on the `typ` and the `size` field. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] -pub struct HeaderTag { +pub struct HeaderTagHeader { + typ: HeaderTagType, /* u16 */ // u16 value - typ: HeaderTagType, - // u16 value - flags: HeaderTagFlag, + flags: HeaderTagFlag, /* u16 */ size: u32, - // maybe additional fields (tag specific) + // Followed by optional additional tag specific fields. } -impl HeaderTag { +impl HeaderTagHeader { + /// Creates a new header. + #[must_use] + pub const fn new(typ: HeaderTagType, flags: HeaderTagFlag, size: u32) -> Self { + Self { typ, flags, size } + } + + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { self.typ } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { self.flags } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { self.size } @@ -88,10 +104,10 @@ impl HeaderTag { #[cfg(test)] mod tests { - use crate::HeaderTag; + use crate::HeaderTagHeader; #[test] fn test_assert_size() { - assert_eq!(core::mem::size_of::(), 2 + 2 + 4); + assert_eq!(core::mem::size_of::(), 2 + 2 + 4); } } diff --git a/multiboot2-header/src/uefi_bs.rs b/multiboot2-header/src/uefi_bs.rs index 93204b3f..ce8b0a32 100644 --- a/multiboot2-header/src/uefi_bs.rs +++ b/multiboot2-header/src/uefi_bs.rs @@ -1,4 +1,4 @@ -use crate::{HeaderTagFlag, HeaderTagType}; +use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType}; use core::mem::size_of; /// This tag indicates that payload supports starting without terminating UEFI boot services. @@ -6,28 +6,33 @@ use core::mem::size_of; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub struct EfiBootServiceHeaderTag { - typ: HeaderTagType, - flags: HeaderTagFlag, - size: u32, + header: HeaderTagHeader, } impl EfiBootServiceHeaderTag { + /// Constructs a new tag. + #[must_use] pub const fn new(flags: HeaderTagFlag) -> Self { - EfiBootServiceHeaderTag { - typ: HeaderTagType::EfiBS, - flags, - size: size_of::() as u32, - } + let header = HeaderTagHeader::new(HeaderTagType::EfiBS, flags, size_of::() as u32); + Self { header } } + /// Returns the [`HeaderTagType`]. + #[must_use] pub const fn typ(&self) -> HeaderTagType { - self.typ + self.header.typ() } + + /// Returns the [`HeaderTagFlag`]s. + #[must_use] pub const fn flags(&self) -> HeaderTagFlag { - self.flags + self.header.flags() } + + /// Returns the size. + #[must_use] pub const fn size(&self) -> u32 { - self.size + self.header.size() } } From 9b2532dc6ecc030740800789651f61770b515df0 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 16:24:58 +0200 Subject: [PATCH 16/35] ci: cancel previous runs when new pushes were made This is more what is naturally expected. It saves CI time and it often prevents many already started runs that will fail anyway from running longer than necessary. See https://stackoverflow.com/questions/66335225 --- .github/workflows/rust.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e0a81671..3c914f55 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,6 +11,10 @@ name: "Cargo workspace" # Run on every push (tag, branch) and pull_request on: [ pull_request, push, workflow_dispatch, merge_group ] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always From 5def169369fee6e52d4ff8bcdaa1dd93e0549504 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:51:52 +0200 Subject: [PATCH 17/35] =?UTF-8?q?multiboot2:=20massive=20internal=20refact?= =?UTF-8?q?oring=20+=20most=20tests=20pass=20Miri=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The goal of this commit is to prepare 100% memory safety (using Miri passing tests as reference). For that, we need a significant under-the-hood changes. `GenericTag` is the generalization of all tags as DST that owns all memory of the tag. This tag can be created from bytes, thus, we can ensure a lifetime and a valid memory range. This tag then can be casted to specific implementations of `TagTrait`. We now never have to increase or decrease the size of the referenced memory during a Tag cast, as the GenericTag already holds all bytes that the more specific type needs. Assertions and the memory checks along the way ensure that nothing can co wrong. Further, the creation of various test data structures was modified to fulfill the new guarantees, that are given in real-world scenarios and are also what the compiler expects. --- multiboot2/Cargo.toml | 4 +- multiboot2/Changelog.md | 2 + multiboot2/src/boot_information.rs | 16 +- multiboot2/src/boot_loader_name.rs | 63 ++-- multiboot2/src/builder/boxed_dst.rs | 45 ++- multiboot2/src/builder/information.rs | 24 +- multiboot2/src/command_line.rs | 59 ++- multiboot2/src/efi.rs | 12 +- multiboot2/src/elf_sections.rs | 24 +- multiboot2/src/end.rs | 4 +- multiboot2/src/framebuffer.rs | 8 +- multiboot2/src/image_load_addr.rs | 4 +- multiboot2/src/lib.rs | 95 ++--- multiboot2/src/memory_map.rs | 18 +- multiboot2/src/module.rs | 74 ++-- multiboot2/src/rsdp.rs | 6 +- multiboot2/src/smbios.rs | 61 ++-- multiboot2/src/tag.rs | 492 +++++++++++++++++++------- multiboot2/src/tag_trait.rs | 25 +- multiboot2/src/test_util.rs | 87 +++++ multiboot2/src/util.rs | 90 +++++ multiboot2/src/vbe_info.rs | 4 +- 22 files changed, 785 insertions(+), 432 deletions(-) create mode 100644 multiboot2/src/test_util.rs create mode 100644 multiboot2/src/util.rs diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index 4a7732de..c01e8b1b 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -45,12 +45,14 @@ bitflags.workspace = true derive_more.workspace = true log.workspace = true +ptr_meta = { version = "~0.2", default-features = false } # We only use a very basic type definition from this crate. To prevent MSRV # bumps from uefi-raw, I restrict this here. Upstream users are likely to have # two versions of this library in it, which is no problem, as we only use the # type definition. uefi-raw = { version = "~0.5", default-features = false } -ptr_meta = { version = "~0.2", default-features = false } + +[dev-dependencies] [package.metadata.docs.rs] all-features = true diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index ecb0b7a4..3951b01d 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -5,6 +5,8 @@ - **Breaking** All functions that returns something useful are now `#[must_use]` - **Breaking** More public fields in tags were replaced by public getters, such as `SmbiosTag::major()` +- **BREAKING:** `multiboot2::{StringError};` -> \ + `multiboot2::util::{StringError};` - updated dependencies - MSRV is 1.75 - documentation enhancements diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 550842db..42a46702 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -277,7 +277,7 @@ impl<'a> BootInformation<'a> { pub fn elf_sections(&self) -> Option { let tag = self.get_tag::(); tag.map(|t| { - assert!((t.entry_size * t.shndx) <= t.size); + assert!((t.entry_size * t.shndx) <= t.size() as u32); t.sections() }) } @@ -359,7 +359,7 @@ impl<'a> BootInformation<'a> { /// special handling is required. This is reflected by code-comments. /// /// ```no_run - /// use multiboot2::{BootInformation, BootInformationHeader, StringError, Tag, TagTrait, TagType, TagTypeId}; + /// use multiboot2::{BootInformation, BootInformationHeader, parse_slice_as_string, StringError, TagHeader, TagTrait, TagType, TagTypeId}; /// /// #[repr(C)] /// #[derive(multiboot2::Pointee)] // Only needed for DSTs. @@ -374,17 +374,17 @@ impl<'a> BootInformation<'a> { /// impl TagTrait for CustomTag { /// const ID: TagType = TagType::Custom(0x1337); /// - /// fn dst_size(base_tag: &Tag) -> usize { + /// fn dst_len(header: &TagHeader) -> usize { /// // The size of the sized portion of the custom tag. /// let tag_base_size = 8; // id + size is 8 byte in size - /// assert!(base_tag.size >= 8); - /// base_tag.size as usize - tag_base_size + /// assert!(header.size >= 8); + /// header.size as usize - tag_base_size /// } /// } /// /// impl CustomTag { /// fn name(&self) -> Result<&str, StringError> { - /// Tag::parse_slice_as_string(&self.name) + /// parse_slice_as_string(&self.name) /// } /// } /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader; @@ -398,8 +398,8 @@ impl<'a> BootInformation<'a> { #[must_use] pub fn get_tag(&'a self) -> Option<&'a TagT> { self.tags() - .find(|tag| tag.typ == TagT::ID) - .map(|tag| tag.cast_tag::()) + .find(|tag| tag.header().typ == TagT::ID) + .map(|tag| tag.cast::()) } /// Returns an iterator over all tags. diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index e6817d26..e2d6c8a1 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,13 +1,13 @@ //! Module for [`BootLoaderNameTag`]. -use crate::tag::{StringError, TagHeader}; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::tag::TagHeader; +use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::(); /// The bootloader name tag. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -61,7 +61,7 @@ impl BootLoaderNameTag { /// } /// ``` pub fn name(&self) -> Result<&str, StringError> { - Tag::parse_slice_as_string(&self.name) + parse_slice_as_string(&self.name) } } @@ -78,54 +78,49 @@ impl Debug for BootLoaderNameTag { impl TagTrait for BootLoaderNameTag { const ID: TagType = TagType::BootLoaderName; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } #[cfg(test)] mod tests { - use crate::{BootLoaderNameTag, Tag, TagTrait, TagType}; - - const MSG: &str = "hello"; - - /// Returns the tag structure in bytes in little endian format. - fn get_bytes() -> std::vec::Vec { - // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string - let size = (4 + 4 + MSG.as_bytes().len() + 1) as u32; - [ - &((TagType::BootLoaderName.val()).to_le_bytes()), - &size.to_le_bytes(), - MSG.as_bytes(), - // Null Byte - &[0], - ] - .iter() - .flat_map(|bytes| bytes.iter()) - .copied() - .collect() + use super::*; + use crate::tag::{GenericTag, TagBytesRef}; + use crate::test_util::AlignedBytes; + + #[rustfmt::skip] + fn get_bytes() -> AlignedBytes<16> { + AlignedBytes::new([ + TagType::BootLoaderName.val() as u8, 0, 0, 0, + 15, 0, 0, 0, + b'h', b'e', b'l', b'l', b'o', b'\0', + /* padding */ + 0, 0 + ]) } /// Tests to parse a string with a terminating null byte from the tag (as the spec defines). #[test] - #[cfg_attr(miri, ignore)] fn test_parse_str() { - let tag = get_bytes(); - let tag = unsafe { &*tag.as_ptr().cast::() }; - let tag = tag.cast_tag::(); + let bytes = get_bytes(); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::BootLoaderName); - assert_eq!(tag.name().expect("must be valid UTF-8"), MSG); + assert_eq!(tag.name(), Ok("hello")); } /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] + #[ignore] fn test_build_str() { - let tag = BootLoaderNameTag::new(MSG); + let tag = BootLoaderNameTag::new("hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, get_bytes()); - assert_eq!(tag.name(), Ok(MSG)); + assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(tag.name(), Ok("hello")); // test also some bigger message let tag = BootLoaderNameTag::new("AbCdEfGhUjK YEAH"); diff --git a/multiboot2/src/builder/boxed_dst.rs b/multiboot2/src/builder/boxed_dst.rs index f433104e..b19b1391 100644 --- a/multiboot2/src/builder/boxed_dst.rs +++ b/multiboot2/src/builder/boxed_dst.rs @@ -1,6 +1,7 @@ //! Module for [`BoxedDst`]. -use crate::{Tag, TagTrait, TagTypeId}; +use crate::util::increase_to_alignment; +use crate::{TagHeader, TagTrait, TagTypeId}; use alloc::alloc::alloc; use core::alloc::Layout; use core::marker::PhantomData; @@ -40,14 +41,10 @@ impl + ?Sized> BoxedDst { let tag_size = size_of::() + size_of::() + content.len(); - // By using miri, I could figure out that there often are problems where - // miri thinks an allocation is smaller then necessary. Most probably - // due to not packed structs. Using packed structs however - // (especially with DSTs), is a crazy ass pain and unusable :/ Therefore, - // the best solution I can think of is to allocate a few byte more than - // necessary. I think that during runtime, everything works fine and - // that no memory issues are present. - let alloc_size = (tag_size + 7) & !7; // align to next 8 byte boundary + // The size of [the allocation for] a value is always a multiple of its + // alignment. + // https://doc.rust-lang.org/reference/type-layout.html + let alloc_size = increase_to_alignment(tag_size); let layout = Layout::from_size_align(alloc_size, ALIGN).unwrap(); let ptr = unsafe { alloc(layout) }; assert!(!ptr.is_null()); @@ -70,8 +67,8 @@ impl + ?Sized> BoxedDst { } } - let base_tag = unsafe { &*ptr.cast::() }; - let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_size(base_tag)); + let base_tag = unsafe { &*ptr.cast::() }; + let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_len(base_tag)); Self { ptr: NonNull::new(raw).unwrap(), @@ -101,10 +98,12 @@ impl PartialEq for BoxedDst { } #[cfg(test)] +#[cfg(not(miri))] mod tests { use super::*; - use crate::tag::StringError; - use crate::TagType; + use crate::test_util::AlignedBytes; + use crate::{parse_slice_as_string, StringError, TagHeader, TagType}; + use core::borrow::Borrow; const METADATA_SIZE: usize = 8; @@ -118,25 +117,24 @@ mod tests { impl CustomTag { fn string(&self) -> Result<&str, StringError> { - Tag::parse_slice_as_string(&self.string) + parse_slice_as_string(&self.string) } } impl TagTrait for CustomTag { const ID: TagType = TagType::Custom(0x1337); - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } #[test] fn test_boxed_dst_tag() { - let content = b"hallo\0"; + let content = AlignedBytes::new(*b"hallo\0"); let content_rust_str = "hallo"; - - let tag = BoxedDst::::new(content); + let tag = BoxedDst::::new(content.borrow()); assert_eq!(tag.typ, CustomTag::ID); assert_eq!(tag.size as usize, METADATA_SIZE + content.len()); assert_eq!(tag.string(), Ok(content_rust_str)); @@ -145,12 +143,9 @@ mod tests { #[test] fn can_hold_tag_trait() { const fn consume(_: &T) {} - let content = b"hallo\0"; - - let tag = BoxedDst::::new(content); + let content = AlignedBytes::new(*b"hallo\0"); + let tag = BoxedDst::::new(content.borrow()); consume(tag.deref()); consume(&*tag); - // Compiler not smart enough? - // consume(&tag); } } diff --git a/multiboot2/src/builder/information.rs b/multiboot2/src/builder/information.rs index 91b70eff..bf05f426 100644 --- a/multiboot2/src/builder/information.rs +++ b/multiboot2/src/builder/information.rs @@ -1,5 +1,6 @@ //! Exports item [`InformationBuilder`]. use crate::builder::{AsBytes, BoxedDst}; +use crate::util::increase_to_alignment; use crate::{ BasicMemoryInfoTag, BootInformationHeader, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, @@ -78,12 +79,6 @@ impl InformationBuilder { Self(Vec::new()) } - /// Returns the provided number or the next multiple of 8. This is helpful - /// to ensure that the following tag starts at a 8-byte aligned boundary. - const fn size_or_up_aligned(size: usize) -> usize { - (size + 7) & !7 - } - /// Returns the expected length of the boot information, when the /// [`Self::build`]-method is called. This function assumes that the begin /// of the boot information is 8-byte aligned and automatically adds padding @@ -92,10 +87,8 @@ impl InformationBuilder { pub fn expected_len(&self) -> usize { let tag_size_iter = self.0.iter().map(|(_, bytes)| bytes.len()); - let payload_tags_size = tag_size_iter.fold(0, |acc, tag_size| { - // size_or_up_aligned: make sure next tag is 8-byte aligned - acc + Self::size_or_up_aligned(tag_size) - }); + let payload_tags_size = + tag_size_iter.fold(0, |acc, tag_size| acc + increase_to_alignment(tag_size)); size_of::() + payload_tags_size + size_of::() } @@ -112,7 +105,7 @@ impl InformationBuilder { if tag_type != TagType::End { let size = tag_serialized.len(); - let size_to_8_align = Self::size_or_up_aligned(size); + let size_to_8_align = increase_to_alignment(size); let size_to_8_align_diff = size_to_8_align - size; // fill zeroes so that next data block is 8-byte aligned dest_buf.extend([0].repeat(size_to_8_align_diff)); @@ -316,6 +309,7 @@ impl InformationBuilder { } #[cfg(test)] +#[cfg(not(miri))] mod tests { use crate::builder::information::InformationBuilder; use crate::{BasicMemoryInfoTag, BootInformation, CommandLineTag, ModuleTag}; @@ -349,14 +343,6 @@ mod tests { builder } - #[test] - fn test_size_or_up_aligned() { - assert_eq!(0, InformationBuilder::size_or_up_aligned(0)); - assert_eq!(8, InformationBuilder::size_or_up_aligned(1)); - assert_eq!(8, InformationBuilder::size_or_up_aligned(8)); - assert_eq!(16, InformationBuilder::size_or_up_aligned(9)); - } - /// Test of the `build` method in isolation specifically for miri to check /// for memory issues. #[test] diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index b48eeddb..cea436a7 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -1,14 +1,14 @@ //! Module for [`CommandLineTag`]. -use crate::tag::{StringError, TagHeader}; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::tag::TagHeader; +use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; use core::str; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::(); /// This tag contains the command line string. /// @@ -55,7 +55,7 @@ impl CommandLineTag { /// } /// ``` pub fn cmdline(&self) -> Result<&str, StringError> { - Tag::parse_slice_as_string(&self.cmdline) + parse_slice_as_string(&self.cmdline) } } @@ -72,54 +72,49 @@ impl Debug for CommandLineTag { impl TagTrait for CommandLineTag { const ID: TagType = TagType::Cmdline; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } #[cfg(test)] mod tests { use super::*; + use crate::tag::{GenericTag, TagBytesRef}; + use crate::test_util::AlignedBytes; - const MSG: &str = "hello"; - - /// Returns the tag structure in bytes in little endian format. - fn get_bytes() -> std::vec::Vec { - // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string - let size = (4 + 4 + MSG.as_bytes().len() + 1) as u32; - [ - &((TagType::Cmdline.val()).to_le_bytes()), - &size.to_le_bytes(), - MSG.as_bytes(), - // Null Byte - &[0], - ] - .iter() - .flat_map(|bytes| bytes.iter()) - .copied() - .collect() + #[rustfmt::skip] + fn get_bytes() -> AlignedBytes<16> { + AlignedBytes::new([ + TagType::Cmdline.val() as u8, 0, 0, 0, + 14, 0, 0, 0, + b'h', b'e', b'l', b'l', b'o', b'\0', + /* padding */ + 0, 0 + ]) } /// Tests to parse a string with a terminating null byte from the tag (as the spec defines). #[test] - #[cfg_attr(miri, ignore)] fn test_parse_str() { - let tag = get_bytes(); - let tag = unsafe { &*tag.as_ptr().cast::() }; - let tag = tag.cast_tag::(); + let bytes = get_bytes(); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Cmdline); - assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG); + assert_eq!(tag.cmdline(), Ok("hello")); } /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] + #[ignore] fn test_build_str() { - let tag = CommandLineTag::new(MSG); + let tag = CommandLineTag::new("hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, get_bytes()); - assert_eq!(tag.cmdline(), Ok(MSG)); + assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(tag.cmdline(), Ok("hello")); // test also some bigger message let tag = CommandLineTag::new("AbCdEfGhUjK YEAH"); diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index 863b8535..59f2fda4 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -7,7 +7,7 @@ //! - [`EFIBootServicesNotExitedTag`] use crate::tag::TagHeader; -use crate::{Tag, TagTrait, TagType}; +use crate::{TagTrait, TagType}; use core::mem::size_of; /// EFI system table in 32 bit mode tag. @@ -38,7 +38,7 @@ impl EFISdt32Tag { impl TagTrait for EFISdt32Tag { const ID: TagType = TagType::Efi32; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// EFI system table in 64 bit mode tag. @@ -69,7 +69,7 @@ impl EFISdt64Tag { impl TagTrait for EFISdt64Tag { const ID: TagType = TagType::Efi64; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// Tag that contains the pointer to the boot loader's UEFI image handle @@ -102,7 +102,7 @@ impl EFIImageHandle32Tag { impl TagTrait for EFIImageHandle32Tag { const ID: TagType = TagType::Efi32Ih; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// Tag that contains the pointer to the boot loader's UEFI image handle @@ -135,7 +135,7 @@ impl EFIImageHandle64Tag { impl TagTrait for EFIImageHandle64Tag { const ID: TagType = TagType::Efi64Ih; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// EFI ExitBootServices was not called tag. @@ -166,7 +166,7 @@ impl Default for EFIBootServicesNotExitedTag { impl TagTrait for EFIBootServicesNotExitedTag { const ID: TagType = TagType::EfiBs; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } #[cfg(all(test, feature = "builder"))] diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 849b08e5..d0d4f4a0 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -2,12 +2,12 @@ #[cfg(feature = "builder")] use crate::builder::BoxedDst; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagHeader, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; use core::str::Utf8Error; -const METADATA_SIZE: usize = mem::size_of::() + 4 * mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); /// This tag contains the section header table from an ELF binary. // The sections iterator is provided via the [`ElfSectionsTag::sections`] @@ -15,8 +15,7 @@ const METADATA_SIZE: usize = mem::size_of::() + 4 * mem::size_of:: usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } impl Debug for ElfSectionsTag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("ElfSectionsTag") - .field("typ", &{ self.typ }) - .field("size", &{ self.size }) - .field("number_of_sections", &{ self.number_of_sections }) - .field("entry_size", &{ self.entry_size }) - .field("shndx", &{ self.shndx }) + .field("typ", &self.header.typ) + .field("size", &self.header.size) + .field("number_of_sections", &self.number_of_sections) + .field("entry_size", &self.entry_size) + .field("shndx", &self.shndx) .field("sections", &self.sections()) .finish() } @@ -85,6 +84,7 @@ impl Debug for ElfSectionsTag { /// An iterator over some ELF sections. #[derive(Clone)] +/// TODO make this memory safe with lifetime capture. pub struct ElfSectionIter { current_section: *const u8, remaining_sections: u32, diff --git a/multiboot2/src/end.rs b/multiboot2/src/end.rs index e48b2238..f5dff6dd 100644 --- a/multiboot2/src/end.rs +++ b/multiboot2/src/end.rs @@ -1,6 +1,6 @@ //! Module for [`EndTag`]. -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagHeader, TagTrait, TagType, TagTypeId}; /// The end tag ends the information struct. #[repr(C)] @@ -22,7 +22,7 @@ impl Default for EndTag { impl TagTrait for EndTag { const ID: TagType = TagType::End; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } #[cfg(test)] diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 50167b88..28447b59 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -1,7 +1,7 @@ //! Module for [`FramebufferTag`]. use crate::tag::TagHeader; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagTrait, TagType, TagTypeId}; use core::fmt::Debug; use core::mem; use core::slice; @@ -183,9 +183,9 @@ impl FramebufferTag { impl TagTrait for FramebufferTag { const ID: TagType = TagType::Framebuffer; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index b11127d4..fc5d060c 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::{Tag, TagTrait, TagType}; +use crate::{TagTrait, TagType}; #[cfg(feature = "builder")] use core::mem::size_of; @@ -36,7 +36,7 @@ impl ImageLoadPhysAddrTag { impl TagTrait for ImageLoadPhysAddrTag { const ID: TagType = TagType::LoadBaseAddr; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } #[cfg(all(test, feature = "builder"))] diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 46a41d5e..b83ef8ed 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -12,7 +12,7 @@ // now allow a few rules which are denied by the above statement // --> They are either ridiculous, not necessary, or we can't fix them. #![allow(clippy::multiple_crate_versions)] -#![deny(missing_docs)] +// #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rustdoc::all)] // --- END STYLE CHECKS --- @@ -56,6 +56,8 @@ extern crate bitflags; #[cfg(feature = "builder")] pub mod builder; +#[cfg(test)] +pub(crate) mod test_util; mod boot_information; mod boot_loader_name; @@ -72,6 +74,7 @@ mod smbios; mod tag; mod tag_trait; mod tag_type; +pub(crate) mod util; mod vbe_info; pub use boot_information::{BootInformation, BootInformationHeader, MbiLoadError}; @@ -94,9 +97,10 @@ pub use module::{ModuleIter, ModuleTag}; pub use ptr_meta::Pointee; pub use rsdp::{RsdpV1Tag, RsdpV2Tag}; pub use smbios::SmbiosTag; -pub use tag::{StringError, Tag}; +pub use tag::TagHeader; pub use tag_trait::TagTrait; pub use tag_type::{TagType, TagTypeId}; +pub use util::{parse_slice_as_string, StringError}; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, @@ -107,11 +111,13 @@ pub use vbe_info::{ /// machine state. pub const MAGIC: u32 = 0x36d76289; +/// The required alignment for tags and the boot information. +pub const ALIGNMENT: usize = 8; + #[cfg(test)] mod tests { use super::*; - use crate::memory_map::MemoryAreaType; - use crate::tag::StringError; + use crate::test_util::AlignedBytes; /// 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 @@ -119,10 +125,7 @@ mod tests { #[test] fn boot_information_is_send_and_sync() { fn accept(_: T) {} - - #[repr(C, align(8))] - struct Bytes([u8; 16]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 16, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 0, 0, 0, 0, // end tag type @@ -138,9 +141,7 @@ mod tests { #[test] fn no_tags() { - #[repr(C, align(8))] - struct Bytes([u8; 16]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 16, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 0, 0, 0, 0, // end tag type @@ -163,9 +164,7 @@ mod tests { #[test] #[should_panic] fn invalid_total_size() { - #[repr(C, align(8))] - struct Bytes([u8; 15]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 15, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 0, 0, 0, 0, // end tag type @@ -188,9 +187,7 @@ mod tests { #[test] #[should_panic] fn invalid_end_tag() { - #[repr(C, align(8))] - struct Bytes([u8; 16]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 16, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 0, 0, 0, 0, // end tag type @@ -211,11 +208,8 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] fn name_tag() { - #[repr(C, align(8))] - struct Bytes([u8; 32]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 32, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 2, 0, 0, 0, // boot loader name tag type @@ -246,14 +240,11 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] fn framebuffer_tag_rgb() { // direct RGB mode test: // taken from GRUB2 running in QEMU at // 1280x720 with 32bpp in BGRA format. - #[repr(C, align(8))] - struct Bytes([u8; 56]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 56, 0, 0, 0, // total size 0, 0, 0, 0, // reserved 8, 0, 0, 0, // framebuffer tag type @@ -307,14 +298,11 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] fn framebuffer_tag_indexed() { // indexed mode test: // this is synthetic, as I can't get QEMU // to run in indexed color mode. - #[repr(C, align(8))] - struct Bytes([u8; 64]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 64, 0, 0, 0, // total size 0, 0, 0, 0, // reserved 8, 0, 0, 0, // framebuffer tag type @@ -379,13 +367,10 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] #[allow(clippy::cognitive_complexity)] fn vbe_info_tag() { //Taken from GRUB2 running in QEMU. - #[repr(C, align(8))] - struct Bytes([u8; 800]); - let bytes = Bytes([ + let bytes = AlignedBytes([ 32, 3, 0, 0, // Total size. 0, 0, 0, 0, // Reserved 7, 0, 0, 0, // Tag type. @@ -551,11 +536,10 @@ mod tests { /// Tests to parse a MBI that was statically extracted from a test run with /// GRUB as bootloader. #[test] + // TODO fix Miri #[cfg_attr(miri, ignore)] fn grub2() { - #[repr(C, align(8))] - struct Bytes([u8; 960]); - let mut bytes: Bytes = Bytes([ + let mut bytes = AlignedBytes([ 192, 3, 0, 0, // total_size 0, 0, 0, 0, // reserved 1, 0, 0, 0, // boot command tag type @@ -959,15 +943,14 @@ mod tests { } #[test] + // TODO fix Miri #[cfg_attr(miri, ignore)] fn elf_sections() { - #[repr(C, align(8))] - struct Bytes([u8; 168]); - let mut bytes: Bytes = Bytes([ + let mut bytes = AlignedBytes([ 168, 0, 0, 0, // total_size 0, 0, 0, 0, // reserved 9, 0, 0, 0, // elf symbols tag type - 20, 2, 0, 0, // elf symbols tag size + 148, 0, 0, 0, // elf symbols tag size 2, 0, 0, 0, // elf symbols num 64, 0, 0, 0, // elf symbols entsize 1, 0, 0, 0, // elf symbols shndx @@ -1036,12 +1019,9 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] fn efi_memory_map() { - #[repr(C, align(8))] - struct Bytes([u8; 80]); // test that the EFI memory map is detected. - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 80, 0, 0, 0, // size 0, 0, 0, 0, // reserved 17, 0, 0, 0, // EFI memory map type @@ -1117,7 +1097,6 @@ mod tests { /// Example for a custom tag. #[test] - #[cfg_attr(miri, ignore)] fn get_custom_tag_from_mbi() { #[repr(C, align(8))] struct CustomTag { @@ -1129,13 +1108,10 @@ mod tests { impl TagTrait for CustomTag { const ID: TagType = TagType::Custom(0x1337); - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_tag_header: &TagHeader) {} } - - #[repr(C, align(8))] - struct AlignedBytes([u8; 32]); // Raw bytes of a MBI that only contains the custom tag. - let bytes: AlignedBytes = AlignedBytes([ + let bytes = AlignedBytes([ 32, 0, 0, @@ -1183,7 +1159,6 @@ mod tests { /// Example for a custom DST tag. #[test] - #[cfg_attr(miri, ignore)] fn get_custom_dst_tag_from_mbi() { #[repr(C)] #[derive(crate::Pointee)] @@ -1195,25 +1170,22 @@ mod tests { impl CustomTag { fn name(&self) -> Result<&str, StringError> { - Tag::parse_slice_as_string(&self.name) + parse_slice_as_string(&self.name) } } impl TagTrait for CustomTag { const ID: TagType = TagType::Custom(0x1337); - fn dst_size(base_tag: &Tag) -> usize { + fn dst_len(header: &TagHeader) -> usize { // The size of the sized portion of the command line tag. let tag_base_size = 8; - assert!(base_tag.size >= 8); - base_tag.size as usize - tag_base_size + assert!(header.size >= 8); + header.size as usize - tag_base_size } } - - #[repr(C, align(8))] - struct AlignedBytes([u8; 32]); // Raw bytes of a MBI that only contains the custom tag. - let bytes: AlignedBytes = AlignedBytes([ + let bytes = AlignedBytes([ 32, 0, 0, @@ -1261,11 +1233,8 @@ mod tests { /// Tests that `get_tag` can consume multiple types that implement `Into` #[test] - #[cfg_attr(miri, ignore)] fn get_tag_into_variants() { - #[repr(C, align(8))] - struct Bytes([u8; 32]); - let bytes: Bytes = Bytes([ + let bytes = AlignedBytes([ 32, 0, 0, diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index b246de56..171cef3d 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -6,14 +6,14 @@ pub use uefi_raw::table::boot::MemoryDescriptor as EFIMemoryDesc; pub use uefi_raw::table::boot::MemoryType as EFIMemoryAreaType; use crate::tag::TagHeader; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagTrait, TagType, TagTypeId}; use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; use core::mem; #[cfg(feature = "builder")] use {crate::builder::AsBytes, crate::builder::BoxedDst}; -const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); /// This tag provides an initial host memory map (legacy boot, not UEFI). /// @@ -75,9 +75,9 @@ impl MemoryMapTag { impl TagTrait for MemoryMapTag { const ID: TagType = TagType::Mmap; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - let size = base_tag.size as usize - METADATA_SIZE; + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + let size = header.size as usize - METADATA_SIZE; assert_eq!(size % mem::size_of::(), 0); size / mem::size_of::() } @@ -287,7 +287,7 @@ impl BasicMemoryInfoTag { impl TagTrait for BasicMemoryInfoTag { const ID: TagType = TagType::BasicMeminfo; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } const EFI_METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); @@ -408,9 +408,9 @@ impl Debug for EFIMemoryMapTag { impl TagTrait for EFIMemoryMapTag { const ID: TagType = TagType::EfiMmap; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= EFI_METADATA_SIZE); - base_tag.size as usize - EFI_METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= EFI_METADATA_SIZE); + header.size as usize - EFI_METADATA_SIZE } } diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 5dd65047..612fbf64 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -1,13 +1,13 @@ //! Module for [`ModuleTag`]. -use crate::tag::{StringError, TagHeader, TagIter}; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::tag::{TagHeader, TagIter}; +use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; #[cfg(feature = "builder")] use {crate::builder::BoxedDst, alloc::vec::Vec}; -const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); +const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); /// The module tag can occur multiple times and specifies passed boot modules /// (blobs in memory). The tag itself doesn't include the blog, but references @@ -51,7 +51,7 @@ impl ModuleTag { /// /// If the function returns `Err` then perhaps the memory is invalid. pub fn cmdline(&self) -> Result<&str, StringError> { - Tag::parse_slice_as_string(&self.cmdline) + parse_slice_as_string(&self.cmdline) } /// Start address of the module. @@ -76,9 +76,9 @@ impl ModuleTag { impl TagTrait for ModuleTag { const ID: TagType = TagType::Module; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } @@ -111,8 +111,8 @@ impl<'a> Iterator for ModuleIter<'a> { fn next(&mut self) -> Option<&'a ModuleTag> { self.iter - .find(|tag| tag.typ == TagType::Module) - .map(|tag| tag.cast_tag()) + .find(|tag| tag.header().typ == TagType::Module) + .map(|tag| tag.cast()) } } @@ -128,49 +128,45 @@ impl<'a> Debug for ModuleIter<'a> { #[cfg(test)] mod tests { - use crate::{ModuleTag, Tag, TagTrait, TagType}; - - const MSG: &str = "hello"; - - /// Returns the tag structure in bytes in little endian format. - fn get_bytes() -> std::vec::Vec { - // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string - // 4 bytes mod_start + 4 bytes mod_end - let size = (4 + 4 + 4 + 4 + MSG.as_bytes().len() + 1) as u32; - [ - &((TagType::Module.val()).to_le_bytes()), - &size.to_le_bytes(), - &0_u32.to_le_bytes(), - &1_u32.to_le_bytes(), - MSG.as_bytes(), - // Null Byte - &[0], - ] - .iter() - .flat_map(|bytes| bytes.iter()) - .copied() - .collect() + use super::*; + use crate::tag::{GenericTag, TagBytesRef}; + use crate::test_util::AlignedBytes; + + #[rustfmt::skip] + fn get_bytes() -> AlignedBytes<24> { + AlignedBytes::new([ + TagType::Module.val() as u8, 0, 0, 0, + 22, 0, 0, 0, + /* mod start */ + 0x00, 0xff, 0, 0, + /* mod end */ + 0xff, 0xff, 0, 0, + b'h', b'e', b'l', b'l', b'o', b'\0', + /* padding */ + 0, 0, + ]) } /// Tests to parse a string with a terminating null byte from the tag (as the spec defines). #[test] - #[cfg_attr(miri, ignore)] fn test_parse_str() { - let tag = get_bytes(); - let tag = unsafe { &*tag.as_ptr().cast::() }; - let tag = tag.cast_tag::(); + let bytes = get_bytes(); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Module); - assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG); + assert_eq!(tag.cmdline(), Ok("hello")); } /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] + #[ignore] fn test_build_str() { - let tag = ModuleTag::new(0, 1, MSG); + let tag = ModuleTag::new(0xff00, 0xffff, "hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, get_bytes()); - assert_eq!(tag.cmdline(), Ok(MSG)); + assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(tag.cmdline(), Ok("hello")); // test also some bigger message let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH"); diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index 8a4c289b..a894a155 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -13,7 +13,7 @@ //! use crate::tag::TagHeader; -use crate::{Tag, TagTrait, TagType}; +use crate::{TagTrait, TagType}; #[cfg(feature = "builder")] use core::mem::size_of; use core::slice; @@ -94,7 +94,7 @@ impl RsdpV1Tag { impl TagTrait for RsdpV1Tag { const ID: TagType = TagType::AcpiV1; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// This tag contains a copy of RSDP as defined per ACPI 2.0 or later specification. @@ -191,5 +191,5 @@ impl RsdpV2Tag { impl TagTrait for RsdpV2Tag { const ID: TagType = TagType::AcpiV2; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index e66b732e..093bb3da 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -3,12 +3,11 @@ #[cfg(feature = "builder")] use crate::builder::BoxedDst; use crate::tag::TagHeader; -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagTrait, TagType}; use core::fmt::Debug; use core::mem; -const METADATA_SIZE: usize = - mem::size_of::() + mem::size_of::() + mem::size_of::() * 8; +const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::() * 8; /// This tag contains a copy of SMBIOS tables as well as their version. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -53,9 +52,9 @@ impl SmbiosTag { impl TagTrait for SmbiosTag { const ID: TagType = TagType::Smbios; - fn dst_size(base_tag: &Tag) -> usize { - assert!(base_tag.size as usize >= METADATA_SIZE); - base_tag.size as usize - METADATA_SIZE + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= METADATA_SIZE); + header.size as usize - METADATA_SIZE } } @@ -73,41 +72,47 @@ impl Debug for SmbiosTag { #[cfg(test)] mod tests { use super::*; + use crate::tag::{GenericTag, TagBytesRef}; + use crate::test_util::AlignedBytes; - /// Returns the tag structure in bytes in little endian format. - fn get_bytes() -> std::vec::Vec { - let tables = [0xabu8; 24]; - // size is: 4 bytes for tag + 4 bytes for size + 1 byte for major and minor - // + 6 bytes reserved + the actual tables - let size = (4 + 4 + 1 + 1 + 6 + tables.len()) as u32; - let typ: u32 = TagType::Smbios.into(); - let mut bytes = [typ.to_le_bytes(), size.to_le_bytes()].concat(); - bytes.push(3); - bytes.push(0); - bytes.extend([0; 6]); - bytes.extend(tables); - bytes + #[rustfmt::skip] + fn get_bytes() -> AlignedBytes<32> { + AlignedBytes::new([ + TagType::Smbios.val() as u8, 0, 0, 0, + 25, 0, 0, 0, + /* major */ + 7, + /* minor */ + 42, + /* reserved */ + 0, 0, 0, 0, 0, 0, + /* table data */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + /* padding */ + 0, 0, 0, 0, 0, 0, 0 + ]) } /// Test to parse a given tag. #[test] - #[cfg_attr(miri, ignore)] fn test_parse() { - let tag = get_bytes(); - let tag = unsafe { &*tag.as_ptr().cast::() }; - let tag = tag.cast_tag::(); + let bytes = get_bytes(); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Smbios); - assert_eq!(tag.major, 3); - assert_eq!(tag.minor, 0); - assert_eq!(tag.tables, [0xabu8; 24]); + assert_eq!(tag.major, 7); + assert_eq!(tag.minor, 42); + assert_eq!(&tag.tables, [0, 1, 2, 3, 4, 5, 6, 7, 8]); } /// Test to generate a tag. #[test] #[cfg(feature = "builder")] + #[ignore] fn test_build() { - let tag = SmbiosTag::new(3, 0, &[0xabu8; 24]); + let tag = SmbiosTag::new(7, 42, &[0, 1, 2, 3, 4, 5, 6, 7, 8]); let bytes = tag.as_bytes(); - assert_eq!(bytes, get_bytes()); + assert_eq!(bytes, &get_bytes()[..]); } } diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 7764fc57..3336f145 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -1,45 +1,28 @@ //! Module for the base tag definitions and helper types. //! -//! The relevant exports of this module is [`Tag`]. - -use crate::{TagTrait, TagType, TagTypeId}; -use core::fmt; -use core::fmt::{Debug, Display, Formatter}; -use core::marker::PhantomData; -use core::str::Utf8Error; - -/// Error type describing failures when parsing the string from a tag. -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum StringError { - /// There is no terminating NUL character, although the specification - /// requires one. - MissingNul(core::ffi::FromBytesUntilNulError), - /// The sequence until the first NUL character is not valid UTF-8. - Utf8(Utf8Error), -} +//! The relevant exports of this module are [`TagHeader`], [`GenericTag`], and +//! [`TagIter`]. +//! +//! The (internal) workflow to parse a tag from bytes is the following: +//! - `&[u8]` --> [`TagBytesRef`] +//! - [`TagBytesRef`] --> [`TagHeader`] +//! - [`TagBytesRef`] + [`TagHeader`] --> [`GenericTag`] +//! - [`GenericTag`] --> cast to desired tag -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), - } - } -} +use crate::util::increase_to_alignment; +use crate::{TagTrait, TagType, TagTypeId, ALIGNMENT}; +use core::fmt::{Debug, Formatter}; +use core::mem; +use core::ops::Deref; +use core::ptr; /// The common header that all tags have in common. This type is ABI compatible. +/// It is the sized counterpart of [`GenericTag`]. /// /// Not to be confused with Multiboot header tags, which are something /// different. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[repr(C)] +#[repr(C, align(8))] // Alignment also propagates to all tag types using this. pub struct TagHeader { /// The ABI-compatible [`TagType`]. pub typ: TagTypeId, /* u32 */ @@ -58,141 +41,402 @@ impl TagHeader { } } -/// Common base structure for all tags that can be passed via the Multiboot2 -/// Information Structure (MBI) to a Multiboot2 payload/program/kernel. +/// Wraps a byte slice representing a tag, but guarantees that the memory +/// requirements are fulfilled. /// -/// Can be transformed to any other tag (sized or unsized/DST) via -/// [`Tag::cast_tag`]. +/// This is the only type that can be used to construct a [`GenericTag`]. /// -/// Do not confuse them with the Multiboot2 header tags. They are something -/// different. -#[derive(Clone, Copy)] +/// The main reason for this dedicated type is to create fine-grained unit-tests +/// for Miri. +/// +/// # Memory Requirements (for Multiboot and Rust/Miri) +/// - At least as big as a `size_of()` +/// - at least [`ALIGNMENT`]-aligned +/// - Length is multiple of [`ALIGNMENT`]. In other words, there are enough +/// padding bytes until so that pointer coming right after the last byte +/// is [`ALIGNMENT`]-aligned +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub(crate) struct TagBytesRef<'a>(&'a [u8]); + +impl<'a> TryFrom<&'a [u8]> for TagBytesRef<'a> { + type Error = MemoryError; + + fn try_from(value: &'a [u8]) -> Result { + if value.len() < mem::size_of::() { + return Err(MemoryError::MinLengthNotSatisfied); + } + // Doesn't work as expected: if align_of_val(&value[0]) < ALIGNMENT { + if value.as_ptr().align_offset(ALIGNMENT) != 0 { + return Err(MemoryError::WrongAlignment); + } + let padding_bytes = value.len() % ALIGNMENT; + if padding_bytes != 0 { + return Err(MemoryError::MissingPadding); + } + Ok(Self(value)) + } +} + +impl<'a> Deref for TagBytesRef<'a> { + type Target = &'a [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Errors that occur when constructing a [`TagBytesRef`]. +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub enum MemoryError { + /// The memory must be at least [`ALIGNMENT`]-aligned. + WrongAlignment, + /// The memory must cover at least the length of a [`TagHeader`]. + MinLengthNotSatisfied, + /// The buffer misses the terminating padding to the next alignment + /// boundary. + // This is mainly relevant to satisfy Miri. As the spec also mandates an + // alignment, we can rely on this property. + MissingPadding, +} + +/// A generic tag serving as base to cast to specific tags. This is a DST +/// version of [`TagHeader`] that solves various type and memory safety +/// problems by having a type that owns the whole memory of a tag. +#[derive(Eq, Ord, PartialEq, PartialOrd, ptr_meta::Pointee)] #[repr(C)] -#[allow(missing_docs)] // type will be removed soon anyway in its form -pub struct Tag { - pub typ: TagTypeId, // u32 - pub size: u32, - // followed by additional, tag specific fields +pub struct GenericTag { + header: TagHeader, + /// Payload of the tag that is reflected in the `size` attribute, thus, no + /// padding bytes! + payload: [u8], } -impl Tag { - /// Returns the underlying type of the tag. - #[must_use] - pub fn typ(&self) -> TagType { - self.typ.into() +impl GenericTag { + /// Base size of the DST struct without the dynamic part. + const BASE_SIZE: usize = mem::size_of::(); + + /// Creates a reference to a [`GenericTag`] from the provided `bytes` + /// [`TagBytesRef`]. + pub(crate) fn ref_from(bytes: TagBytesRef) -> &Self { + let header = bytes.as_ptr().cast::(); + let header = unsafe { &*header }; + let dst_len = Self::dst_len(header); + assert_eq!(header.size as usize, Self::BASE_SIZE + dst_len); + + let generic_tag: *const GenericTag = + ptr_meta::from_raw_parts(bytes.as_ptr().cast(), dst_len); + let generic_tag = unsafe { &*generic_tag }; + + generic_tag } - /// Casts the base tag to the specific tag type. - #[must_use] - pub fn cast_tag<'a, T: TagTrait + ?Sized + 'a>(&'a self) -> &'a T { - assert_eq!(self.typ, T::ID); - // Safety: At this point, we trust that "self.size" and the size hint - // for DST tags are sane. - unsafe { TagTrait::from_base_tag(self) } + pub fn header(&self) -> &TagHeader { + &self.header } - /// Parses the provided byte sequence as Multiboot string, which maps to a - /// [`str`]. - pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> { - let cstr = core::ffi::CStr::from_bytes_until_nul(bytes).map_err(StringError::MissingNul)?; + pub fn payload(&self) -> &[u8] { + &self.payload + } - cstr.to_str().map_err(StringError::Utf8) + /// Casts the generic tag to a specific [`TagTrait`] implementation which + /// may be a ZST or DST typed tag. + pub fn cast(&self) -> &T { + let base_ptr = ptr::addr_of!(*self); + let t_dst_size = T::dst_len(&self.header); + let t_ptr = ptr_meta::from_raw_parts(base_ptr.cast(), t_dst_size); + let t_ref = unsafe { &*t_ptr }; + assert_eq!(mem::size_of_val(self), mem::size_of_val(t_ref)); + t_ref } } -impl Debug for Tag { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let tag_type = TagType::from(self.typ); - - let mut debug = f.debug_struct("Tag"); - debug.field("typ", &tag_type); - - if !matches!(tag_type, TagType::Custom(_)) { - debug.field("typ (numeric)", &(u32::from(self.typ))); - } +impl Debug for GenericTag { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("GenericTag") + .field("header", &self.header) + .field("payload", &"") + .finish() + } +} - debug.field("size", &(self.size)); +impl TagTrait for GenericTag { + const ID: TagType = TagType::Custom(0xffffffff); - debug.finish() + fn dst_len(header: &TagHeader) -> usize { + assert!(header.size as usize >= Self::BASE_SIZE); + header.size as usize - Self::BASE_SIZE } } -/// Iterates the MBI's tags from the first tag to the end tag. +/// Iterates the tags of the MBI from the first tag to the end tag. THe end tag +/// included. #[derive(Clone, Debug)] pub struct TagIter<'a> { - /// Pointer to the next tag. Updated in each iteration. - pub current: *const Tag, - /// The pointer right after the MBI. Used for additional bounds checking. - end_ptr_exclusive: *const u8, - /// Lifetime capture of the MBI's memory. - _mem: PhantomData<&'a ()>, + /// Absolute offset to next tag and updated in each iteration. + next_tag_offset: usize, + exclusive_end: *const u8, + buffer: &'a [u8], } impl<'a> TagIter<'a> { /// Creates a new iterator pub fn new(mem: &'a [u8]) -> Self { + // Assert alignment. assert_eq!(mem.as_ptr().align_offset(8), 0); + + let exclusive_end = unsafe { mem.as_ptr().add(mem.len()) }; + TagIter { - current: mem.as_ptr().cast(), - end_ptr_exclusive: unsafe { mem.as_ptr().add(mem.len()) }, - _mem: PhantomData, + next_tag_offset: 0, + buffer: mem, + exclusive_end, } } } impl<'a> Iterator for TagIter<'a> { - type Item = &'a Tag; + type Item = &'a GenericTag; - fn next(&mut self) -> Option<&'a Tag> { - // This never failed so far. But better be safe. - assert!(self.current.cast::() < self.end_ptr_exclusive); + fn next(&mut self) -> Option { + let next_ptr = unsafe { self.buffer.as_ptr().add(self.next_tag_offset) }; - let tag = unsafe { &*self.current }; - match tag.typ() { - TagType::End => None, // end tag - _ => { - // We return the tag and update self.current already to the next - // tag. + if next_ptr == self.exclusive_end { + return None; + } + assert!(next_ptr < self.exclusive_end); - // next pointer (rounded up to 8-byte alignment) - let ptr_offset = (tag.size as usize + 7) & !7; + let next_tag_ptr = next_ptr.cast::(); - // go to next tag - self.current = unsafe { self.current.cast::().add(ptr_offset).cast::() }; + let tag_hdr = unsafe { &*next_tag_ptr }; - Some(tag) - } - } + // Get relevant byte portion for the next tag. This includes padding + // bytes to fulfill Rust memory guarantees. Otherwise, Miri complains. + // See . + let bytes = { + let from = self.next_tag_offset; + let to = from + tag_hdr.size as usize; + // The size of [the allocation for] a value is always a multiple of its + // alignment. + // https://doc.rust-lang.org/reference/type-layout.html + let to = increase_to_alignment(to); + + // Update ptr for next iteration. + self.next_tag_offset += to - from; + + &self.buffer[from..to] + }; + + // Should never fail at this point. + let tag_bytes = TagBytesRef::try_from(bytes).unwrap(); + + Some(GenericTag::ref_from(tag_bytes)) } } #[cfg(test)] mod tests { use super::*; + use crate::test_util::AlignedBytes; + use core::mem; + + #[test] + fn test_new_generic_tag() { + let bytes = AlignedBytes::new([ + /* id: 0xffff_ffff */ + 0xff_u8, 0xff_u8, 0xff_u8, 0xff_u8, /* id: 16 */ + 16, 0, 0, 0, /* field a: 0xdead_beef */ + 0xde, 0xad, 0xbe, 0xef, /* field b: 0x1337_1337 */ + 0x13, 0x37, 0x13, 0x37, + ]); + + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + assert_eq!(tag.header.typ, 0xffff_ffff); + assert_eq!(tag.header.size, 16); + assert_eq!(tag.payload.len(), 8); + } + + #[test] + fn test_cast_generic_tag_to_sized_tag() { + #[repr(C)] + struct CustomTag { + tag_header: TagHeader, + a: u32, + b: u32, + } + + impl TagTrait for CustomTag { + const ID: TagType = TagType::End; + + fn dst_len(_header: &TagHeader) -> Self::Metadata {} + } + + let bytes = AlignedBytes([ + /* id: 0xffff_ffff */ + 0xff_u8, 0xff_u8, 0xff_u8, 0xff_u8, /* id: 16 */ + 16, 0, 0, 0, /* field a: 0xdead_beef */ + 0xef, 0xbe, 0xad, 0xde, /* field b: 0x1337_1337 */ + 0x37, 0x13, 0x37, 0x13, + ]); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let custom_tag = tag.cast::(); + + assert_eq!(mem::size_of_val(custom_tag), 16); + assert_eq!(custom_tag.a, 0xdead_beef); + assert_eq!(custom_tag.b, 0x1337_1337); + } + + #[test] + fn test_cast_generic_tag_to_dynamically_sized_tag() { + #[repr(C)] + #[derive(ptr_meta::Pointee)] + struct CustomDstTag { + tag_header: TagHeader, + a: u32, + payload: [u8], + } + + impl TagTrait for CustomDstTag { + const ID: TagType = TagType::End; + + fn dst_len(header: &TagHeader) -> Self::Metadata { + let base_size = mem::size_of::() + mem::size_of::(); + header.size as usize - base_size + } + } + + let bytes = AlignedBytes([ + /* id: 0xffff_ffff */ + 0xff_u8, 0xff_u8, 0xff_u8, 0xff_u8, /* id: 16 */ + 16, 0, 0, 0, /* field a: 0xdead_beef */ + 0xef, 0xbe, 0xad, 0xde, /* field b: 0x1337_1337 */ + 0x37, 0x13, 0x37, 0x13, + ]); + + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + let custom_tag = tag.cast::(); + + assert_eq!(mem::size_of_val(custom_tag), 16); + assert_eq!(custom_tag.a, 0xdead_beef); + assert_eq!(custom_tag.payload.len(), 4); + assert_eq!(custom_tag.payload[0], 0x37); + assert_eq!(custom_tag.payload[1], 0x13); + assert_eq!(custom_tag.payload[2], 0x37); + assert_eq!(custom_tag.payload[3], 0x13); + } + + #[test] + fn test_tag_bytes_ref() { + let empty: &[u8] = &[]; + assert_eq!( + TagBytesRef::try_from(empty), + Err(MemoryError::MinLengthNotSatisfied) + ); + + let slice = &[0_u8, 1, 2, 3, 4, 5, 6]; + assert_eq!( + TagBytesRef::try_from(&slice[..]), + Err(MemoryError::MinLengthNotSatisfied) + ); + + let slice = AlignedBytes([0_u8, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0]); + // Guaranteed wrong alignment + let unaligned_slice = &slice[3..]; + assert_eq!( + TagBytesRef::try_from(&unaligned_slice[..]), + Err(MemoryError::WrongAlignment) + ); + + let slice = AlignedBytes([0_u8, 1, 2, 3, 4, 5, 6, 7]); + let slice = &slice[..]; + assert_eq!(TagBytesRef::try_from(slice), Ok(TagBytesRef(slice))); + } #[test] - fn parse_slice_as_string() { - // empty slice is invalid - assert!(matches!( - Tag::parse_slice_as_string(&[]), - Err(StringError::MissingNul(_)) - )); - // empty string is fine - assert_eq!(Tag::parse_slice_as_string(&[0x00]), Ok("")); - // reject invalid utf8 - assert!(matches!( - Tag::parse_slice_as_string(&[0xff, 0x00]), - Err(StringError::Utf8(_)) - )); - // reject missing null - assert!(matches!( - Tag::parse_slice_as_string(b"hello"), - Err(StringError::MissingNul(_)) - )); - // must not include final null - assert_eq!(Tag::parse_slice_as_string(b"hello\0"), Ok("hello")); - assert_eq!(Tag::parse_slice_as_string(b"hello\0\0"), Ok("hello")); - // must skip everytihng after first null - assert_eq!(Tag::parse_slice_as_string(b"hello\0foo"), Ok("hello")); + fn test_create_generic_tag() { + #[rustfmt::skip] + let bytes = AlignedBytes::new( + [ + TagType::Cmdline.val() as u8, 0, 0, 0, + /* Tag size */ + 18, 0, 0, 0, + /* Some payload. */ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, + // Padding + 0, 0, 0, 0, 0, 0 + ], + ); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + assert_eq!(tag.header.typ, TagType::Cmdline); + assert_eq!(tag.header.size, 8 + 10); + } + + #[test] + fn test_cast_generic_tag_to_generic_tag() { + #[rustfmt::skip] + let bytes = AlignedBytes::new( + [ + TagType::Cmdline.val() as u8, 0, 0, 0, + /* Tag size */ + 18, 0, 0, 0, + /* Some payload. */ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, + // Padding + 0, 0, 0, 0, 0, 0 + ], + ); + let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let tag = GenericTag::ref_from(bytes); + + // Main objective here is also that this test passes Miri. + let tag = tag.cast::(); + assert_eq!(tag.header.typ, TagType::Cmdline); + assert_eq!(tag.header.size, 8 + 10); + } + + #[test] + fn test_tag_iter() { + #[rustfmt::skip] + let bytes = AlignedBytes::new( + [ + /* Some minimal tag. */ + 0xff, 0, 0, 0, + 8, 0, 0, 0, + /* Some tag with payload. */ + 0xfe, 0, 0, 0, + 12, 0, 0, 0, + 1, 2, 3, 4, + // Padding + 0, 0, 0, 0, + /* End tag */ + 0, 0, 0, 0, + 8, 0, 0, 0, + ], + ); + let mut iter = TagIter::new(&bytes[..]); + let first = iter.next().unwrap(); + assert_eq!(first.header.typ, TagType::Custom(0xff)); + assert_eq!(first.header.size, 8); + assert!(first.payload.is_empty()); + + let second = iter.next().unwrap(); + assert_eq!(second.header.typ, TagType::Custom(0xfe)); + assert_eq!(second.header.size, 12); + assert_eq!(&second.payload, &[1, 2, 3, 4]); + + let third = iter.next().unwrap(); + assert_eq!(third.header.typ, TagType::End); + assert_eq!(third.header.size, 8); + assert!(first.payload.is_empty()); + + assert_eq!(iter.next(), None); } } diff --git a/multiboot2/src/tag_trait.rs b/multiboot2/src/tag_trait.rs index c13b63d1..1d44d72f 100644 --- a/multiboot2/src/tag_trait.rs +++ b/multiboot2/src/tag_trait.rs @@ -1,10 +1,11 @@ //! Module for [`TagTrait`]. -use crate::{Tag, TagType}; +use crate::tag::TagHeader; +use crate::TagType; use ptr_meta::Pointee; /// A trait to abstract over all sized and unsized tags (DSTs). For sized tags, -/// this trait does not much. For DSTs, a [`TagTrait::dst_size`] implementation +/// this trait does not much. For DSTs, a [`TagTrait::dst_len`] implementation /// must be provided, which returns the right size hint for the dynamically /// sized portion of the struct. /// @@ -22,12 +23,12 @@ pub trait TagTrait: Pointee { /// /// For sized tags, this just returns `()`. For DSTs, this returns an /// `usize`. - fn dst_size(base_tag: &Tag) -> Self::Metadata; + fn dst_len(header: &TagHeader) -> Self::Metadata; /// Returns the tag as the common base tag structure. - fn as_base_tag(&self) -> &Tag { + fn as_base_tag(&self) -> &TagHeader { let ptr = core::ptr::addr_of!(*self); - unsafe { &*ptr.cast::() } + unsafe { &*ptr.cast::() } } /// Returns the total size of the tag. The depends on the `size` field of @@ -43,18 +44,4 @@ pub trait TagTrait: Pointee { let ptr = core::ptr::addr_of!(*self); unsafe { core::slice::from_raw_parts(ptr.cast(), self.size()) } } - - /// Creates a reference to a (dynamically sized) tag type in a safe way. - /// DST tags need to implement a proper [`Self::dst_size`] implementation. - /// - /// # Safety - /// Callers must be sure that the "size" field of the provided [`Tag`] is - /// sane and the underlying memory valid. The implementation of this trait - /// **must have** a correct [`Self::dst_size`] implementation. - #[must_use] - unsafe fn from_base_tag(tag: &Tag) -> &Self { - let ptr = core::ptr::addr_of!(*tag); - let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag)); - &*ptr - } } diff --git a/multiboot2/src/test_util.rs b/multiboot2/src/test_util.rs new file mode 100644 index 00000000..a56156d0 --- /dev/null +++ b/multiboot2/src/test_util.rs @@ -0,0 +1,87 @@ +//! Various test utilities. + +use crate::ALIGNMENT; +use core::borrow::Borrow; +use core::ops::Deref; + +/// Helper to 8-byte align the underlying bytes, as mandated in the Multiboot2 +/// spec. With this type, one can create manual and raw Multiboot2 boot +/// information or just the bytes for simple tags, in a manual and raw approach. +#[cfg(test)] +#[repr(C, align(8))] +pub(crate) struct AlignedBytes(pub [u8; N]); + +impl AlignedBytes { + pub const fn new(bytes: [u8; N]) -> Self { + Self(bytes) + } +} + +impl Borrow<[u8; N]> for AlignedBytes { + fn borrow(&self) -> &[u8; N] { + &self.0 + } +} + +impl Borrow<[u8]> for AlignedBytes { + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for AlignedBytes { + type Target = [u8; N]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// The tests down below are all Miri-approved. +#[cfg(test)] +mod tests { + use super::*; + use crate::tag::TagBytesRef; + use core::mem; + use core::ptr::addr_of; + + #[test] + fn abi() { + let bytes = AlignedBytes([0]); + assert_eq!(mem::align_of_val(&bytes), ALIGNMENT); + assert_eq!(bytes.as_ptr().align_offset(8), 0); + assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); + + let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7]); + assert_eq!(mem::align_of_val(&bytes), ALIGNMENT); + assert_eq!(bytes.as_ptr().align_offset(8), 0); + assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); + + let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!(mem::align_of_val(&bytes), ALIGNMENT); + assert_eq!(bytes.as_ptr().align_offset(8), 0); + assert_eq!((addr_of!(bytes[0])).align_offset(8), 0); + assert_eq!((addr_of!(bytes[7])).align_offset(8), 1); + assert_eq!((addr_of!(bytes[8])).align_offset(8), 0); + assert_eq!((addr_of!(bytes[9])).align_offset(8), 7); + } + + #[test] + fn compatible_with_tag_bytes_ref_type() { + #[rustfmt::skip] + let bytes = AlignedBytes( + [ + /* tag id */ + 0, 0, 0, 0, + /* size */ + 14, 0, 0, 0, + /* arbitrary payload */ + 1, 2, 3, 4, + 5, 6, + /* padding */ + 0, 0, + ] + ); + let _a = TagBytesRef::try_from(&bytes[..]).unwrap(); + } +} diff --git a/multiboot2/src/util.rs b/multiboot2/src/util.rs new file mode 100644 index 00000000..a6504cf3 --- /dev/null +++ b/multiboot2/src/util.rs @@ -0,0 +1,90 @@ +//! Various utilities. + +use crate::ALIGNMENT; +use core::fmt; +use core::fmt::{Display, Formatter}; +use core::str::Utf8Error; + +/// Error type describing failures when parsing the string from a tag. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum StringError { + /// There is no terminating NUL character, although the specification + /// requires one. + MissingNul(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), + } + } +} + +/// Parses the provided byte sequence as Multiboot string, which maps to a +/// [`str`]. +pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> { + let cstr = core::ffi::CStr::from_bytes_until_nul(bytes).map_err(StringError::MissingNul)?; + cstr.to_str().map_err(StringError::Utf8) +} + +/// Increases the given size to the next alignment boundary, if it is not a +/// multiple of the alignment yet. This is relevant as in Rust's [type layout], +/// the allocated size of a type is always a multiple of the alignment, even +/// if the type is smaller. +/// +/// [type layout]: https://doc.rust-lang.org/reference/type-layout.html +pub const fn increase_to_alignment(size: usize) -> usize { + let mask = ALIGNMENT - 1; + (size + mask) & !mask +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_slice_as_string() { + // empty slice is invalid + assert!(matches!( + parse_slice_as_string(&[]), + Err(StringError::MissingNul(_)) + )); + // empty string is fine + assert_eq!(parse_slice_as_string(&[0x00]), Ok("")); + // reject invalid utf8 + assert!(matches!( + parse_slice_as_string(&[0xff, 0x00]), + Err(StringError::Utf8(_)) + )); + // reject missing null + assert!(matches!( + parse_slice_as_string(b"hello"), + Err(StringError::MissingNul(_)) + )); + // 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 + assert_eq!(parse_slice_as_string(b"hello\0foo"), Ok("hello")); + } + + #[test] + fn test_increase_to_alignment() { + assert_eq!(increase_to_alignment(0), 0); + assert_eq!(increase_to_alignment(1), 8); + assert_eq!(increase_to_alignment(7), 8); + assert_eq!(increase_to_alignment(8), 8); + assert_eq!(increase_to_alignment(9), 16); + } +} diff --git a/multiboot2/src/vbe_info.rs b/multiboot2/src/vbe_info.rs index a5973f4c..6389d760 100644 --- a/multiboot2/src/vbe_info.rs +++ b/multiboot2/src/vbe_info.rs @@ -1,6 +1,6 @@ //! Module for [`VBEInfoTag`]. -use crate::{Tag, TagTrait, TagType, TagTypeId}; +use crate::{TagHeader, TagTrait, TagType, TagTypeId}; use core::fmt; /// This tag contains VBE metadata, VBE controller information returned by the @@ -42,7 +42,7 @@ pub struct VBEInfoTag { impl TagTrait for VBEInfoTag { const ID: TagType = TagType::Vbe; - fn dst_size(_base_tag: &Tag) {} + fn dst_len(_: &TagHeader) {} } /// VBE controller information. From ecfa1ab15334ab2b2b4286bb45771bab880d847a Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:51:58 +0200 Subject: [PATCH 18/35] multiboot2: align(8) for all tags This was already transitively the case as soon as they have a `header: TagHeader` field, however, for perfection, this can now safely be added. --- multiboot2/src/boot_information.rs | 4 ++-- multiboot2/src/boot_loader_name.rs | 2 +- multiboot2/src/command_line.rs | 2 +- multiboot2/src/efi.rs | 13 +++++++------ multiboot2/src/elf_sections.rs | 2 +- multiboot2/src/end.rs | 2 +- multiboot2/src/framebuffer.rs | 2 +- multiboot2/src/image_load_addr.rs | 2 +- multiboot2/src/memory_map.rs | 2 +- multiboot2/src/module.rs | 2 +- multiboot2/src/rsdp.rs | 4 ++-- multiboot2/src/smbios.rs | 2 +- multiboot2/src/vbe_info.rs | 2 +- 13 files changed, 21 insertions(+), 20 deletions(-) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 42a46702..f014db3c 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -38,7 +38,7 @@ impl core::error::Error for MbiLoadError {} /// The basic header of a [`BootInformation`] as sized Rust type. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct BootInformationHeader { // size is multiple of 8 total_size: u32, @@ -68,7 +68,7 @@ impl AsBytes for BootInformationHeader {} /// This type holds the whole data of the MBI. This helps to better satisfy miri /// when it checks for memory issues. #[derive(ptr_meta::Pointee)] -#[repr(C)] +#[repr(C, align(8))] struct BootInformationInner { header: BootInformationHeader, tags: [u8], diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index e2d6c8a1..81a006fc 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -11,7 +11,7 @@ const METADATA_SIZE: usize = mem::size_of::(); /// The bootloader name tag. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct BootLoaderNameTag { header: TagHeader, /// Null-terminated UTF-8 string diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index cea436a7..419934ff 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -15,7 +15,7 @@ const METADATA_SIZE: usize = mem::size_of::(); /// The string is a normal C-style UTF-8 zero-terminated string that can be /// obtained via the `command_line` method. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct CommandLineTag { header: TagHeader, /// Null-terminated UTF-8 string diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index 59f2fda4..4c8d727a 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -12,7 +12,7 @@ use core::mem::size_of; /// EFI system table in 32 bit mode tag. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EFISdt32Tag { header: TagHeader, pointer: u32, @@ -43,7 +43,7 @@ impl TagTrait for EFISdt32Tag { /// EFI system table in 64 bit mode tag. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EFISdt64Tag { header: TagHeader, pointer: u64, @@ -75,7 +75,7 @@ impl TagTrait for EFISdt64Tag { /// Tag that contains the pointer to the boot loader's UEFI image handle /// (32-bit). #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EFIImageHandle32Tag { header: TagHeader, pointer: u32, @@ -108,7 +108,7 @@ impl TagTrait for EFIImageHandle32Tag { /// Tag that contains the pointer to the boot loader's UEFI image handle /// (64-bit). #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EFIImageHandle64Tag { header: TagHeader, pointer: u64, @@ -138,9 +138,10 @@ impl TagTrait for EFIImageHandle64Tag { fn dst_len(_: &TagHeader) {} } -/// EFI ExitBootServices was not called tag. +/// EFI ExitBootServices was not called tag. This tag has no payload and is +/// just a marker. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct EFIBootServicesNotExitedTag { header: TagHeader, } diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index d0d4f4a0..a084b434 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -13,7 +13,7 @@ const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::() /// The VBE Framebuffer information tag. #[derive(ptr_meta::Pointee, Eq)] -#[repr(C)] +#[repr(C, align(8))] pub struct FramebufferTag { header: TagHeader, diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index fc5d060c..7248d782 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -9,7 +9,7 @@ use core::mem::size_of; /// binary was relocated, for example if the relocatable header tag was /// specified. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct ImageLoadPhysAddrTag { header: TagHeader, load_base_addr: u32, diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index 171cef3d..fcab800e 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -26,7 +26,7 @@ const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::() + 2 * mem::size_of::() + mem::size_of::() /// This tag contains a copy of SMBIOS tables as well as their version. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct SmbiosTag { header: TagHeader, major: u8, diff --git a/multiboot2/src/vbe_info.rs b/multiboot2/src/vbe_info.rs index 6389d760..961cc883 100644 --- a/multiboot2/src/vbe_info.rs +++ b/multiboot2/src/vbe_info.rs @@ -6,7 +6,7 @@ use core::fmt; /// This tag contains VBE metadata, VBE controller information returned by the /// VBE Function 00h and VBE mode information returned by the VBE Function 01h. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(C, align(8))] pub struct VBEInfoTag { typ: TagTypeId, length: u32, From 5148bbb3dc6c0761fe0e763214d8d170707254c5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:51:59 +0200 Subject: [PATCH 19/35] multiboot2: Memory-safe new_boxed This will replace the existing BoxedDst typ, which has UB. --- multiboot2/src/lib.rs | 2 +- multiboot2/src/util.rs | 49 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index b83ef8ed..8fffb090 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -100,7 +100,7 @@ pub use smbios::SmbiosTag; pub use tag::TagHeader; pub use tag_trait::TagTrait; pub use tag_type::{TagType, TagTypeId}; -pub use util::{parse_slice_as_string, StringError}; +pub use util::{new_boxed, parse_slice_as_string, StringError}; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, diff --git a/multiboot2/src/util.rs b/multiboot2/src/util.rs index a6504cf3..e59b1365 100644 --- a/multiboot2/src/util.rs +++ b/multiboot2/src/util.rs @@ -1,9 +1,13 @@ //! Various utilities. -use crate::ALIGNMENT; +use crate::tag::GenericTag; +use crate::{TagHeader, TagTrait, TagType, ALIGNMENT}; use core::fmt; use core::fmt::{Display, Formatter}; use core::str::Utf8Error; +use core::{ptr, slice}; +#[cfg(feature = "builder")] +use {alloc::alloc::Layout, alloc::boxed::Box}; /// Error type describing failures when parsing the string from a tag. #[derive(Debug, PartialEq, Eq, Clone)] @@ -31,6 +35,38 @@ impl core::error::Error for StringError { } } +/// Creates a new tag implementing [`TagTrait`] on the heap. This works for +/// sized and unsized tags. However, it only makes sense to use this for tags +/// that are DSTs (unsized), as for the sized ones, you can call a regular +/// constructor and box the result. +/// +/// # Parameters +/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that +/// are included into the tag. +#[cfg(feature = "alloc")] +pub fn new_boxed(additional_bytes: &[u8]) -> Box { + let size = size_of::() + additional_bytes.iter().len(); + let alloc_size = increase_to_alignment(size); + let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap(); + let heap_ptr = unsafe { alloc::alloc::alloc(layout) }; + assert!(!heap_ptr.is_null()); + + unsafe { + heap_ptr.cast::().write(T::ID.val()); + heap_ptr.cast::().add(1).write(size as u32); + } + unsafe { + let ptr = heap_ptr.add(size_of::()); + ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len()); + } + + let header = unsafe { heap_ptr.cast::().as_ref() }.unwrap(); + + let ptr = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(header)); + + unsafe { Box::from_raw(ptr) } +} + /// Parses the provided byte sequence as Multiboot string, which maps to a /// [`str`]. pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> { @@ -52,6 +88,8 @@ pub const fn increase_to_alignment(size: usize) -> usize { #[cfg(test)] mod tests { use super::*; + use crate::tag::GenericTag; + use crate::CommandLineTag; #[test] fn test_parse_slice_as_string() { @@ -87,4 +125,13 @@ mod tests { assert_eq!(increase_to_alignment(8), 8); assert_eq!(increase_to_alignment(9), 16); } + + #[test] + fn test_new_boxed() { + let tag = new_boxed::(&[0, 1, 2, 3]); + assert_eq!(tag.header().typ, GenericTag::ID); + {} + let tag = new_boxed::("hello\0".as_bytes()); + assert_eq!(tag.cmdline(), Ok("hello")); + } } From e5cccec2599c426ea4544793c276065aa6c8e2c1 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:00 +0200 Subject: [PATCH 20/35] multiboot2: remove BoxedDst + run builder tests by Miri --- multiboot2/src/boot_loader_name.rs | 13 +-- multiboot2/src/builder/boxed_dst.rs | 151 -------------------------- multiboot2/src/builder/information.rs | 29 +++-- multiboot2/src/builder/mod.rs | 3 - multiboot2/src/command_line.rs | 9 +- multiboot2/src/elf_sections.rs | 13 +-- multiboot2/src/framebuffer.rs | 6 +- multiboot2/src/lib.rs | 8 +- multiboot2/src/memory_map.rs | 14 +-- multiboot2/src/module.rs | 9 +- multiboot2/src/smbios.rs | 11 +- 11 files changed, 50 insertions(+), 216 deletions(-) delete mode 100644 multiboot2/src/builder/boxed_dst.rs diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 81a006fc..62f1bc91 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,11 +1,11 @@ //! Module for [`BootLoaderNameTag`]. use crate::tag::TagHeader; -use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; +use crate::{new_boxed, parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; #[cfg(feature = "builder")] -use {crate::builder::BoxedDst, alloc::vec::Vec}; +use {alloc::boxed::Box, alloc::vec::Vec}; const METADATA_SIZE: usize = mem::size_of::(); @@ -22,13 +22,13 @@ impl BootLoaderNameTag { /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] - pub fn new(name: &str) -> BoxedDst { + pub fn new(name: &str) -> Box { let mut bytes: Vec<_> = name.bytes().collect(); if !bytes.ends_with(&[0]) { // terminating null-byte bytes.push(0); } - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Returns the underlying [`TagType`]. @@ -94,7 +94,7 @@ mod tests { fn get_bytes() -> AlignedBytes<16> { AlignedBytes::new([ TagType::BootLoaderName.val() as u8, 0, 0, 0, - 15, 0, 0, 0, + 14, 0, 0, 0, b'h', b'e', b'l', b'l', b'o', b'\0', /* padding */ 0, 0 @@ -115,11 +115,10 @@ mod tests { /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] - #[ignore] fn test_build_str() { let tag = BootLoaderNameTag::new("hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.name(), Ok("hello")); // test also some bigger message diff --git a/multiboot2/src/builder/boxed_dst.rs b/multiboot2/src/builder/boxed_dst.rs deleted file mode 100644 index b19b1391..00000000 --- a/multiboot2/src/builder/boxed_dst.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! Module for [`BoxedDst`]. - -use crate::util::increase_to_alignment; -use crate::{TagHeader, TagTrait, TagTypeId}; -use alloc::alloc::alloc; -use core::alloc::Layout; -use core::marker::PhantomData; -use core::mem::size_of; -use core::ops::Deref; -use core::ptr::NonNull; - -/// A helper type to create boxed DST, i.e., tags with a dynamic size for the -/// builder. This is tricky in Rust. This type behaves similar to the regular -/// `Box` type except that it ensure the same layout is used for the (explicit) -/// allocation and the (implicit) deallocation of memory. Otherwise, I didn't -/// find any way to figure out the right layout for a DST. Miri always reported -/// issues that the deallocation used a wrong layout. -/// -/// Technically, I'm certain this code is memory safe. But with this type, I -/// also can convince miri that it is. -#[derive(Debug, Eq)] -pub struct BoxedDst { - ptr: core::ptr::NonNull, - layout: Layout, - // marker: I used this only as the regular Box impl also does it. - _marker: PhantomData, -} - -impl + ?Sized> BoxedDst { - /// Create a boxed tag with the given content. - /// - /// # Parameters - /// - `content` - All payload bytes of the DST tag without the tag type or - /// the size. The memory is only read and can be discarded - /// afterwards. - pub(crate) fn new(content: &[u8]) -> Self { - // Currently, I do not find a nice way of making this dynamic so that - // also miri is guaranteed to be happy. But it seems that 4 is fine - // here. I do have control over allocation and deallocation. - const ALIGN: usize = 4; - - let tag_size = size_of::() + size_of::() + content.len(); - - // The size of [the allocation for] a value is always a multiple of its - // alignment. - // https://doc.rust-lang.org/reference/type-layout.html - let alloc_size = increase_to_alignment(tag_size); - let layout = Layout::from_size_align(alloc_size, ALIGN).unwrap(); - let ptr = unsafe { alloc(layout) }; - assert!(!ptr.is_null()); - - // write tag content to memory - unsafe { - // write tag type - let ptrx = ptr.cast::(); - ptrx.write(T::ID.into()); - - // write tag size - let ptrx = ptrx.add(1).cast::(); - ptrx.write(tag_size as u32); - - // write rest of content - let ptrx = ptrx.add(1).cast::(); - let tag_content_slice = core::slice::from_raw_parts_mut(ptrx, content.len()); - for (i, &byte) in content.iter().enumerate() { - tag_content_slice[i] = byte; - } - } - - let base_tag = unsafe { &*ptr.cast::() }; - let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_len(base_tag)); - - Self { - ptr: NonNull::new(raw).unwrap(), - layout, - _marker: PhantomData, - } - } -} - -impl Drop for BoxedDst { - fn drop(&mut self) { - unsafe { alloc::alloc::dealloc(self.ptr.as_ptr().cast(), self.layout) } - } -} - -impl Deref for BoxedDst { - type Target = T; - fn deref(&self) -> &Self::Target { - unsafe { self.ptr.as_ref() } - } -} - -impl PartialEq for BoxedDst { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) - } -} - -#[cfg(test)] -#[cfg(not(miri))] -mod tests { - use super::*; - use crate::test_util::AlignedBytes; - use crate::{parse_slice_as_string, StringError, TagHeader, TagType}; - use core::borrow::Borrow; - - const METADATA_SIZE: usize = 8; - - #[derive(ptr_meta::Pointee)] - #[repr(C)] - struct CustomTag { - typ: TagTypeId, - size: u32, - string: [u8], - } - - impl CustomTag { - fn string(&self) -> Result<&str, StringError> { - parse_slice_as_string(&self.string) - } - } - - impl TagTrait for CustomTag { - const ID: TagType = TagType::Custom(0x1337); - - fn dst_len(header: &TagHeader) -> usize { - assert!(header.size as usize >= METADATA_SIZE); - header.size as usize - METADATA_SIZE - } - } - - #[test] - fn test_boxed_dst_tag() { - let content = AlignedBytes::new(*b"hallo\0"); - let content_rust_str = "hallo"; - let tag = BoxedDst::::new(content.borrow()); - assert_eq!(tag.typ, CustomTag::ID); - assert_eq!(tag.size as usize, METADATA_SIZE + content.len()); - assert_eq!(tag.string(), Ok(content_rust_str)); - } - - #[test] - fn can_hold_tag_trait() { - const fn consume(_: &T) {} - let content = AlignedBytes::new(*b"hallo\0"); - let tag = BoxedDst::::new(content.borrow()); - consume(tag.deref()); - consume(&*tag); - } -} diff --git a/multiboot2/src/builder/information.rs b/multiboot2/src/builder/information.rs index bf05f426..5b317bf4 100644 --- a/multiboot2/src/builder/information.rs +++ b/multiboot2/src/builder/information.rs @@ -1,12 +1,13 @@ //! Exports item [`InformationBuilder`]. -use crate::builder::{AsBytes, BoxedDst}; +use crate::builder::AsBytes; use crate::util::increase_to_alignment; use crate::{ BasicMemoryInfoTag, BootInformationHeader, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, - MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, + MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, ALIGNMENT, }; +use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; use core::mem::size_of; @@ -115,8 +116,6 @@ impl InformationBuilder { /// Constructs the bytes for a valid Multiboot2 information with the given properties. #[must_use] pub fn build(self) -> BootInformationBytes { - const ALIGN: usize = 8; - // PHASE 1/2: Prepare Vector // We allocate more than necessary so that we can ensure an correct @@ -134,7 +133,7 @@ impl InformationBuilder { // Unfortunately, it is not possible to reliably test this in a unit // test as long as the allocator_api feature is not stable. // Due to my manual testing, however, it works. - let offset = bytes.as_ptr().align_offset(ALIGN); + let offset = bytes.as_ptr().align_offset(ALIGNMENT); bytes.extend([0].repeat(offset)); // ----------------------------------------------- @@ -182,6 +181,8 @@ impl InformationBuilder { .0 .iter() .map(|(typ, _)| *typ) + // TODO make a type for tag_is_allowed_multiple_times so that we can + // link to it in the doc. .any(|typ| typ == T::ID && !Self::tag_is_allowed_multiple_times(typ)); if is_redundant_tag { @@ -205,13 +206,13 @@ impl InformationBuilder { /// Adds a 'bootloader name' tag (represented by [`BootLoaderNameTag`]) to the builder. #[must_use] - pub fn bootloader_name_tag(self, tag: BoxedDst) -> Self { + pub fn bootloader_name_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'command line' tag (represented by [`CommandLineTag`]) to the builder. #[must_use] - pub fn command_line_tag(self, tag: BoxedDst) -> Self { + pub fn command_line_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } @@ -247,19 +248,19 @@ impl InformationBuilder { /// Adds a 'EFI Memory map' tag (represented by [`EFIMemoryMapTag`]) to the builder. #[must_use] - pub fn efi_memory_map_tag(self, tag: BoxedDst) -> Self { + pub fn efi_memory_map_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'ELF-Symbols' tag (represented by [`ElfSectionsTag`]) to the builder. #[must_use] - pub fn elf_sections_tag(self, tag: BoxedDst) -> Self { + pub fn elf_sections_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'Framebuffer info' tag (represented by [`FramebufferTag`]) to the builder. #[must_use] - pub fn framebuffer_tag(self, tag: BoxedDst) -> Self { + pub fn framebuffer_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } @@ -271,14 +272,14 @@ impl InformationBuilder { /// Adds a (*none EFI*) 'memory map' tag (represented by [`MemoryMapTag`]) to the builder. #[must_use] - pub fn memory_map_tag(self, tag: BoxedDst) -> Self { + pub fn memory_map_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } /// Adds a 'Modules' tag (represented by [`ModuleTag`]) to the builder. /// This tag can occur multiple times in boot information. #[must_use] - pub fn add_module_tag(self, tag: BoxedDst) -> Self { + pub fn add_module_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } @@ -296,7 +297,7 @@ impl InformationBuilder { /// Adds a 'SMBIOS tables' tag (represented by [`SmbiosTag`]) to the builder. #[must_use] - pub fn smbios_tag(self, tag: BoxedDst) -> Self { + pub fn smbios_tag(self, tag: Box) -> Self { self.add_tag(&*tag).unwrap() } @@ -309,7 +310,6 @@ impl InformationBuilder { } #[cfg(test)] -#[cfg(not(miri))] mod tests { use crate::builder::information::InformationBuilder; use crate::{BasicMemoryInfoTag, BootInformation, CommandLineTag, ModuleTag}; @@ -353,7 +353,6 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] fn test_builder() { // Step 1/2: Build MBI let mb2i_data = create_builder().build(); diff --git a/multiboot2/src/builder/mod.rs b/multiboot2/src/builder/mod.rs index 7d06c425..4fd00932 100644 --- a/multiboot2/src/builder/mod.rs +++ b/multiboot2/src/builder/mod.rs @@ -1,10 +1,7 @@ //! Module for the builder-feature. -mod boxed_dst; mod information; -// This must be public to support external people to create boxed DSTs. -pub use boxed_dst::BoxedDst; pub use information::InformationBuilder; /// Helper trait for all structs that need to be serialized that do not diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 419934ff..50f4ee32 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -6,7 +6,7 @@ use core::fmt::{Debug, Formatter}; use core::mem; use core::str; #[cfg(feature = "builder")] -use {crate::builder::BoxedDst, alloc::vec::Vec}; +use {crate::new_boxed, alloc::boxed::Box, alloc::vec::Vec}; const METADATA_SIZE: usize = mem::size_of::(); @@ -26,13 +26,13 @@ impl CommandLineTag { /// Create a new command line tag from the given string. #[cfg(feature = "builder")] #[must_use] - pub fn new(command_line: &str) -> BoxedDst { + pub fn new(command_line: &str) -> Box { let mut bytes: Vec<_> = command_line.bytes().collect(); if !bytes.ends_with(&[0]) { // terminating null-byte bytes.push(0); } - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Reads the command line of the kernel as Rust string slice without @@ -109,11 +109,10 @@ mod tests { /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] - #[ignore] fn test_build_str() { let tag = CommandLineTag::new("hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.cmdline(), Ok("hello")); // test also some bigger message diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index a084b434..eb70c616 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -1,11 +1,11 @@ //! Module for [`ElfSectionsTag`]. -#[cfg(feature = "builder")] -use crate::builder::BoxedDst; use crate::{TagHeader, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; use core::str::Utf8Error; +#[cfg(feature = "builder")] +use {crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); @@ -26,12 +26,7 @@ impl ElfSectionsTag { /// Create a new ElfSectionsTag with the given data. #[cfg(feature = "builder")] #[must_use] - pub fn new( - number_of_sections: u32, - entry_size: u32, - shndx: u32, - sections: &[u8], - ) -> BoxedDst { + pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box { let mut bytes = [ number_of_sections.to_le_bytes(), entry_size.to_le_bytes(), @@ -39,7 +34,7 @@ impl ElfSectionsTag { ] .concat(); bytes.extend_from_slice(sections); - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Get an iterator of loaded ELF sections. diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index a1f7cc0b..56ee4a29 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -7,7 +7,7 @@ use core::mem; use core::slice; use derive_more::Display; #[cfg(feature = "builder")] -use {crate::builder::AsBytes, crate::builder::BoxedDst, alloc::vec::Vec}; +use {crate::builder::AsBytes, crate::new_boxed, alloc::boxed::Box, alloc::vec::Vec}; /// Helper struct to read bytes from a raw pointer and increase the pointer /// automatically. @@ -94,14 +94,14 @@ impl FramebufferTag { height: u32, bpp: u8, buffer_type: FramebufferType, - ) -> BoxedDst { + ) -> Box { let mut bytes: Vec = address.to_le_bytes().into(); bytes.extend(pitch.to_le_bytes()); bytes.extend(width.to_le_bytes()); bytes.extend(height.to_le_bytes()); bytes.extend(bpp.to_le_bytes()); bytes.extend(buffer_type.to_bytes()); - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Contains framebuffer physical address. diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 8fffb090..555fd833 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -298,6 +298,7 @@ mod tests { } #[test] + #[cfg_attr(miri, ignore)] fn framebuffer_tag_indexed() { // indexed mode test: // this is synthetic, as I can't get QEMU @@ -1056,10 +1057,7 @@ mod tests { assert_eq!(desc.phys_start, 0x100000); assert_eq!(desc.page_count, 4); assert_eq!(desc.ty, EFIMemoryAreaType::CONVENTIONAL); - // test that the EFI memory map is not detected if the boot services - // are not exited. - struct Bytes2([u8; 80]); - let bytes2: Bytes2 = Bytes2([ + let bytes2 = AlignedBytes([ 80, 0, 0, 0, // size 0, 0, 0, 0, // reserved 17, 0, 0, 0, // EFI memory map type @@ -1081,7 +1079,7 @@ mod tests { 0, 0, 0, 0, // end tag type. 8, 0, 0, 0, // end tag size. ]); - let bi = unsafe { BootInformation::load(bytes2.0.as_ptr().cast()) }; + let bi = unsafe { BootInformation::load(bytes2.as_ptr().cast()) }; let bi = bi.unwrap(); let efi_mmap = bi.efi_memory_map_tag(); assert!(efi_mmap.is_none()); diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index fcab800e..26a5aaa6 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -11,7 +11,7 @@ use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; use core::mem; #[cfg(feature = "builder")] -use {crate::builder::AsBytes, crate::builder::BoxedDst}; +use {crate::builder::AsBytes, crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); @@ -38,14 +38,14 @@ impl MemoryMapTag { /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] - pub fn new(areas: &[MemoryArea]) -> BoxedDst { + pub fn new(areas: &[MemoryArea]) -> Box { let entry_size: u32 = mem::size_of::().try_into().unwrap(); let entry_version: u32 = 0; let mut bytes = [entry_size.to_le_bytes(), entry_version.to_le_bytes()].concat(); for area in areas { bytes.extend(area.as_bytes()); } - BoxedDst::new(bytes.as_slice()) + new_boxed(bytes.as_slice()) } /// Returns the entry size. @@ -326,7 +326,7 @@ impl EFIMemoryMapTag { /// EFIMemoryDescs, not the ones you might have gotten from the firmware. #[cfg(feature = "builder")] #[must_use] - pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> BoxedDst { + pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box { // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw // release. @@ -354,7 +354,7 @@ impl EFIMemoryMapTag { /// Create a new EFI memory map tag from the given EFI memory map. #[cfg(feature = "builder")] #[must_use] - pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> BoxedDst { + pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> Box { assert!(desc_size > 0); assert_eq!(efi_mmap.len() % desc_size as usize, 0); assert_eq!( @@ -369,7 +369,7 @@ impl EFIMemoryMapTag { efi_mmap, ] .concat(); - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Returns an iterator over the provided memory areas. @@ -466,7 +466,7 @@ impl<'a> ExactSizeIterator for EFIMemoryAreaIter<'a> { } } -#[cfg(all(test, feature = "builder", not(miri)))] +#[cfg(all(test, feature = "builder"))] mod tests { use super::*; use std::mem::size_of; diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index c6748ded..65bc263a 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -5,7 +5,7 @@ use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; #[cfg(feature = "builder")] -use {crate::builder::BoxedDst, alloc::vec::Vec}; +use {crate::new_boxed, alloc::boxed::Box, alloc::vec::Vec}; const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); @@ -26,7 +26,7 @@ impl ModuleTag { /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] - pub fn new(start: u32, end: u32, cmdline: &str) -> BoxedDst { + pub fn new(start: u32, end: u32, cmdline: &str) -> Box { assert!(end > start, "must have a size"); let mut cmdline_bytes: Vec<_> = cmdline.bytes().collect(); @@ -38,7 +38,7 @@ impl ModuleTag { let end_bytes = end.to_le_bytes(); let mut content_bytes = [start_bytes, end_bytes].concat(); content_bytes.extend_from_slice(&cmdline_bytes); - BoxedDst::new(&content_bytes) + new_boxed(&content_bytes) } /// Reads the command line of the boot module as Rust string slice without @@ -161,11 +161,10 @@ mod tests { /// Test to generate a tag from a given string. #[test] #[cfg(feature = "builder")] - #[ignore] fn test_build_str() { let tag = ModuleTag::new(0xff00, 0xffff, "hello"); let bytes = tag.as_bytes(); - assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.cmdline(), Ok("hello")); // test also some bigger message diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 42c95aa5..0bbb6b9b 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -1,11 +1,11 @@ //! Module for [`SmbiosTag`]. -#[cfg(feature = "builder")] -use crate::builder::BoxedDst; use crate::tag::TagHeader; use crate::{TagTrait, TagType}; use core::fmt::Debug; use core::mem; +#[cfg(feature = "builder")] +use {crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::() + mem::size_of::() * 8; @@ -24,10 +24,10 @@ impl SmbiosTag { /// Constructs a new tag. #[cfg(feature = "builder")] #[must_use] - pub fn new(major: u8, minor: u8, tables: &[u8]) -> BoxedDst { + pub fn new(major: u8, minor: u8, tables: &[u8]) -> Box { let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec(); bytes.extend(tables); - BoxedDst::new(&bytes) + new_boxed(&bytes) } /// Returns the major number. @@ -109,10 +109,9 @@ mod tests { /// Test to generate a tag. #[test] #[cfg(feature = "builder")] - #[ignore] fn test_build() { let tag = SmbiosTag::new(7, 42, &[0, 1, 2, 3, 4, 5, 6, 7, 8]); let bytes = tag.as_bytes(); - assert_eq!(bytes, &get_bytes()[..]); + assert_eq!(bytes, &get_bytes()[..tag.size()]); } } From c47b7ac40d1521c7f41d1cc5d7c625f4a72caae6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:00 +0200 Subject: [PATCH 21/35] multiboot2: new_boxed() can be used with less allocations on callee side Note that Miri runs significantly longer with this change. More memory accesses that need to be tracked. --- multiboot2/src/boot_loader_name.rs | 14 +++--- multiboot2/src/command_line.rs | 10 ++--- multiboot2/src/elf_sections.rs | 12 ++--- multiboot2/src/framebuffer.rs | 29 ++++++------ multiboot2/src/memory_map.rs | 71 +++++++++--------------------- multiboot2/src/module.rs | 17 ++++--- multiboot2/src/smbios.rs | 5 +-- multiboot2/src/util.rs | 39 +++++++++++----- 8 files changed, 90 insertions(+), 107 deletions(-) diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 62f1bc91..4603116a 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -2,10 +2,10 @@ use crate::tag::TagHeader; use crate::{new_boxed, parse_slice_as_string, StringError, TagTrait, TagType}; +#[cfg(feature = "builder")] +use alloc::boxed::Box; use core::fmt::{Debug, Formatter}; use core::mem; -#[cfg(feature = "builder")] -use {alloc::boxed::Box, alloc::vec::Vec}; const METADATA_SIZE: usize = mem::size_of::(); @@ -23,12 +23,12 @@ impl BootLoaderNameTag { #[cfg(feature = "builder")] #[must_use] pub fn new(name: &str) -> Box { - let mut bytes: Vec<_> = name.bytes().collect(); - if !bytes.ends_with(&[0]) { - // terminating null-byte - bytes.push(0); + let bytes = name.as_bytes(); + if bytes.ends_with(&[0]) { + new_boxed(&[bytes]) + } else { + new_boxed(&[bytes, &[0]]) } - new_boxed(&bytes) } /// Returns the underlying [`TagType`]. diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 50f4ee32..432b3023 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -27,12 +27,12 @@ impl CommandLineTag { #[cfg(feature = "builder")] #[must_use] pub fn new(command_line: &str) -> Box { - let mut bytes: Vec<_> = command_line.bytes().collect(); - if !bytes.ends_with(&[0]) { - // terminating null-byte - bytes.push(0); + let bytes = command_line.as_bytes(); + if bytes.ends_with(&[0]) { + new_boxed(&[bytes]) + } else { + new_boxed(&[bytes, &[0]]) } - new_boxed(&bytes) } /// Reads the command line of the kernel as Rust string slice without diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index eb70c616..55c6e9e6 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -27,14 +27,10 @@ impl ElfSectionsTag { #[cfg(feature = "builder")] #[must_use] pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box { - let mut bytes = [ - number_of_sections.to_le_bytes(), - entry_size.to_le_bytes(), - shndx.to_le_bytes(), - ] - .concat(); - bytes.extend_from_slice(sections); - new_boxed(&bytes) + let number_of_sections = number_of_sections.to_ne_bytes(); + let entry_size = entry_size.to_ne_bytes(); + let shndx = shndx.to_ne_bytes(); + new_boxed(&[&number_of_sections, &entry_size, &shndx, sections]) } /// Get an iterator of loaded ELF sections. diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 56ee4a29..0083905a 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -95,13 +95,13 @@ impl FramebufferTag { bpp: u8, buffer_type: FramebufferType, ) -> Box { - let mut bytes: Vec = address.to_le_bytes().into(); - bytes.extend(pitch.to_le_bytes()); - bytes.extend(width.to_le_bytes()); - bytes.extend(height.to_le_bytes()); - bytes.extend(bpp.to_le_bytes()); - bytes.extend(buffer_type.to_bytes()); - new_boxed(&bytes) + let address = address.to_ne_bytes(); + let pitch = pitch.to_ne_bytes(); + let width = width.to_ne_bytes(); + let height = height.to_ne_bytes(); + let bpp = bpp.to_ne_bytes(); + let buffer_type = buffer_type.to_bytes(); + new_boxed(&[&address, &pitch, &width, &height, &bpp, &buffer_type]) } /// Contains framebuffer physical address. @@ -145,6 +145,7 @@ impl FramebufferTag { match typ { FramebufferTypeId::Indexed => { let num_colors = reader.read_u32(); + // TODO static cast looks like UB? let palette = unsafe { slice::from_raw_parts( reader.current_address() as *const FramebufferColor, @@ -274,23 +275,23 @@ impl<'a> FramebufferType<'a> { let mut v = Vec::new(); match self { FramebufferType::Indexed { palette } => { - v.extend(0u8.to_le_bytes()); // type - v.extend(0u16.to_le_bytes()); // reserved - v.extend((palette.len() as u32).to_le_bytes()); + v.extend(0u8.to_ne_bytes()); // type + v.extend(0u16.to_ne_bytes()); // reserved + v.extend((palette.len() as u32).to_ne_bytes()); for color in palette.iter() { v.extend(color.as_bytes()); } } FramebufferType::RGB { red, green, blue } => { - v.extend(1u8.to_le_bytes()); // type - v.extend(0u16.to_le_bytes()); // reserved + v.extend(1u8.to_ne_bytes()); // type + v.extend(0u16.to_ne_bytes()); // reserved v.extend(red.as_bytes()); v.extend(green.as_bytes()); v.extend(blue.as_bytes()); } FramebufferType::Text => { - v.extend(2u8.to_le_bytes()); // type - v.extend(0u16.to_le_bytes()); // reserved + v.extend(2u8.to_ne_bytes()); // type + v.extend(0u16.to_ne_bytes()); // reserved } } v diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index 26a5aaa6..3f056c92 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -11,7 +11,7 @@ use core::fmt::{Debug, Formatter}; use core::marker::PhantomData; use core::mem; #[cfg(feature = "builder")] -use {crate::builder::AsBytes, crate::new_boxed, alloc::boxed::Box}; +use {crate::new_boxed, alloc::boxed::Box, core::slice}; const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); @@ -39,13 +39,14 @@ impl MemoryMapTag { #[cfg(feature = "builder")] #[must_use] pub fn new(areas: &[MemoryArea]) -> Box { - let entry_size: u32 = mem::size_of::().try_into().unwrap(); - let entry_version: u32 = 0; - let mut bytes = [entry_size.to_le_bytes(), entry_version.to_le_bytes()].concat(); - for area in areas { - bytes.extend(area.as_bytes()); - } - new_boxed(bytes.as_slice()) + let entry_size = mem::size_of::().to_ne_bytes(); + let entry_version = 0_u32.to_ne_bytes(); + let areas = { + let ptr = areas.as_ptr().cast::(); + let len = areas.len() * size_of::(); + unsafe { slice::from_raw_parts(ptr, len) } + }; + new_boxed(&[&entry_size, &entry_version, areas]) } /// Returns the entry size. @@ -139,9 +140,6 @@ impl Debug for MemoryArea { } } -#[cfg(feature = "builder")] -impl AsBytes for MemoryArea {} - /// ABI-friendly version of [`MemoryAreaType`]. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] @@ -292,9 +290,6 @@ impl TagTrait for BasicMemoryInfoTag { const EFI_METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of::(); -#[cfg(feature = "builder")] -impl AsBytes for EFIMemoryDesc {} - /// EFI memory map tag. The embedded [`EFIMemoryDesc`]s follows the EFI /// specification. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -322,32 +317,19 @@ pub struct EFIMemoryMapTag { impl EFIMemoryMapTag { /// Create a new EFI memory map tag with the given memory descriptors. - /// Version and size can't be set because you're passing a slice of - /// EFIMemoryDescs, not the ones you might have gotten from the firmware. #[cfg(feature = "builder")] #[must_use] pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box { - // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw - // release. - - let size_base = mem::size_of::(); - // Taken from https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059 - let desc_size_diff = mem::size_of::() - size_base % mem::size_of::(); - let desc_size = size_base + desc_size_diff; - - assert!(desc_size >= size_base); - - let mut efi_mmap = alloc::vec::Vec::with_capacity(descs.len() * desc_size); - for desc in descs { - efi_mmap.extend(desc.as_bytes()); - // fill with zeroes - efi_mmap.extend([0].repeat(desc_size_diff)); - } + let efi_mmap = { + let ptr = descs.as_ptr().cast::(); + let len = descs.len() * size_of::(); + unsafe { slice::from_raw_parts(ptr, len) } + }; Self::new_from_map( - desc_size as u32, + mem::size_of::() as u32, EFIMemoryDesc::VERSION, - efi_mmap.as_slice(), + efi_mmap, ) } @@ -355,21 +337,10 @@ impl EFIMemoryMapTag { #[cfg(feature = "builder")] #[must_use] pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> Box { - assert!(desc_size > 0); - assert_eq!(efi_mmap.len() % desc_size as usize, 0); - assert_eq!( - efi_mmap - .as_ptr() - .align_offset(mem::align_of::()), - 0 - ); - let bytes = [ - &desc_size.to_le_bytes(), - &desc_version.to_le_bytes(), - efi_mmap, - ] - .concat(); - new_boxed(&bytes) + assert_ne!(desc_size, 0); + let desc_size = desc_size.to_ne_bytes(); + let desc_version = desc_version.to_ne_bytes(); + new_boxed(&[&desc_size, &desc_version, efi_mmap]) } /// Returns an iterator over the provided memory areas. @@ -491,8 +462,6 @@ mod tests { ]; let efi_mmap_tag = EFIMemoryMapTag::new_from_descs(&descs); - assert_eq!(efi_mmap_tag.desc_size, 48 /* 40 + 8 */); - let mut iter = efi_mmap_tag.memory_areas(); assert_eq!(iter.next(), Some(&descs[0])); diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 65bc263a..75910560 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -29,16 +29,15 @@ impl ModuleTag { pub fn new(start: u32, end: u32, cmdline: &str) -> Box { assert!(end > start, "must have a size"); - let mut cmdline_bytes: Vec<_> = cmdline.bytes().collect(); - if !cmdline_bytes.ends_with(&[0]) { - // terminating null-byte - cmdline_bytes.push(0); + let start = start.to_ne_bytes(); + let end = end.to_ne_bytes(); + let cmdline = cmdline.as_bytes(); + + if cmdline.ends_with(&[0]) { + new_boxed(&[&start, &end, cmdline]) + } else { + new_boxed(&[&start, &end, cmdline, &[0]]) } - let start_bytes = start.to_le_bytes(); - let end_bytes = end.to_le_bytes(); - let mut content_bytes = [start_bytes, end_bytes].concat(); - content_bytes.extend_from_slice(&cmdline_bytes); - new_boxed(&content_bytes) } /// Reads the command line of the boot module as Rust string slice without diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 0bbb6b9b..208268ae 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -25,9 +25,8 @@ impl SmbiosTag { #[cfg(feature = "builder")] #[must_use] pub fn new(major: u8, minor: u8, tables: &[u8]) -> Box { - let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec(); - bytes.extend(tables); - new_boxed(&bytes) + let reserved = [0, 0, 0, 0, 0, 0]; + new_boxed(&[&[major, minor], &reserved, tables]) } /// Returns the major number. diff --git a/multiboot2/src/util.rs b/multiboot2/src/util.rs index e59b1365..8cdaa671 100644 --- a/multiboot2/src/util.rs +++ b/multiboot2/src/util.rs @@ -41,11 +41,17 @@ impl core::error::Error for StringError { /// constructor and box the result. /// /// # Parameters -/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that -/// are included into the tag. +/// - `additional_bytes_slices`: Array of byte slices that should be included +/// without additional padding in-between. You don't need to add the bytes +/// for [`TagHeader`], but only additional ones. #[cfg(feature = "alloc")] -pub fn new_boxed(additional_bytes: &[u8]) -> Box { - let size = size_of::() + additional_bytes.iter().len(); +pub fn new_boxed(additional_bytes_slices: &[&[u8]]) -> Box { + let additional_size = additional_bytes_slices + .iter() + .map(|b| b.len()) + .sum::(); + + let size = size_of::() + additional_size; let alloc_size = increase_to_alignment(size); let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap(); let heap_ptr = unsafe { alloc::alloc::alloc(layout) }; @@ -55,9 +61,16 @@ pub fn new_boxed(additional_bytes: &[u8]) -> Box { heap_ptr.cast::().write(T::ID.val()); heap_ptr.cast::().add(1).write(size as u32); } - unsafe { - let ptr = heap_ptr.add(size_of::()); - ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len()); + + let mut write_offset = size_of::(); + for &bytes in additional_bytes_slices { + unsafe { + let len = bytes.len(); + let src = bytes.as_ptr(); + let dst = heap_ptr.add(write_offset); + ptr::copy_nonoverlapping(src, dst, len); + write_offset += len; + } } let header = unsafe { heap_ptr.cast::().as_ref() }.unwrap(); @@ -128,10 +141,16 @@ mod tests { #[test] fn test_new_boxed() { - let tag = new_boxed::(&[0, 1, 2, 3]); + let tag = new_boxed::(&[&[0, 1, 2, 3]]); assert_eq!(tag.header().typ, GenericTag::ID); - {} - let tag = new_boxed::("hello\0".as_bytes()); + assert_eq!(tag.payload(), &[0, 1, 2, 3]); + + // Test that bytes are added consecutively without gaps. + let tag = new_boxed::(&[&[0], &[1], &[2, 3]]); + assert_eq!(tag.header().typ, GenericTag::ID); + assert_eq!(tag.payload(), &[0, 1, 2, 3]); + + let tag = new_boxed::(&["hello\0".as_bytes()]); assert_eq!(tag.cmdline(), Ok("hello")); } } From 4ee788458316da367d9dd1b4c300258d56a0dc90 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:01 +0200 Subject: [PATCH 22/35] multiboot2: more unit tests --- multiboot2/src/boot_loader_name.rs | 6 ++++++ multiboot2/src/command_line.rs | 6 ++++++ multiboot2/src/module.rs | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 4603116a..3fc1810a 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -121,6 +121,12 @@ mod tests { assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.name(), Ok("hello")); + // With terminating null. + let tag = BootLoaderNameTag::new("hello\0"); + let bytes = tag.as_bytes(); + assert_eq!(bytes, &get_bytes()[..tag.size()]); + assert_eq!(tag.name(), Ok("hello")); + // test also some bigger message let tag = BootLoaderNameTag::new("AbCdEfGhUjK YEAH"); assert_eq!(tag.name(), Ok("AbCdEfGhUjK YEAH")); diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 432b3023..7cbfd06f 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -115,6 +115,12 @@ mod tests { assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.cmdline(), Ok("hello")); + // With terminating null. + let tag = CommandLineTag::new("hello\0"); + let bytes = tag.as_bytes(); + assert_eq!(bytes, &get_bytes()[..tag.size()]); + assert_eq!(tag.cmdline(), Ok("hello")); + // test also some bigger message let tag = CommandLineTag::new("AbCdEfGhUjK YEAH"); assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH")); diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 75910560..d36ee549 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -166,6 +166,12 @@ mod tests { assert_eq!(bytes, &get_bytes()[..tag.size()]); assert_eq!(tag.cmdline(), Ok("hello")); + // With terminating null. + let tag = ModuleTag::new(0xff00, 0xffff, "hello\0"); + let bytes = tag.as_bytes(); + assert_eq!(bytes, &get_bytes()[..tag.size()]); + assert_eq!(tag.cmdline(), Ok("hello")); + // test also some bigger message let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH"); assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH")); From 78d42b86e947c3f62e10307e5ae4ee490496165a Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:01 +0200 Subject: [PATCH 23/35] multiboot2: fix Miri issues in elf_sections() test Miri still outputs a warning tho: "warning: integer-to-pointer cast" Nevertheless, no we are UB free! --- multiboot2/src/boot_information.rs | 2 +- multiboot2/src/elf_sections.rs | 41 +++++++++++------------------- multiboot2/src/lib.rs | 4 --- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index f014db3c..68863af8 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -440,7 +440,7 @@ impl fmt::Debug for BootInformation<'_> { 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().unwrap_or_default()); + debug.field("elf_sections", &self.elf_sections()); } } diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 55c6e9e6..27c4c3c1 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -2,6 +2,7 @@ use crate::{TagHeader, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; +use core::marker::{PhantomData, PhantomPinned}; use core::mem; use core::str::Utf8Error; #[cfg(feature = "builder")] @@ -34,21 +35,18 @@ impl ElfSectionsTag { } /// Get an iterator of loaded ELF sections. - pub(crate) const fn sections(&self) -> ElfSectionIter { + pub(crate) fn sections(&self) -> ElfSectionIter { let string_section_offset = (self.shndx * self.entry_size) as isize; let string_section_ptr = - unsafe { self.first_section().offset(string_section_offset) as *const _ }; + unsafe { self.sections.as_ptr().offset(string_section_offset) as *const _ }; ElfSectionIter { - current_section: self.first_section(), + current_section: self.sections.as_ptr(), remaining_sections: self.number_of_sections, entry_size: self.entry_size, string_section: string_section_ptr, + _phantom_data: PhantomData::default(), } } - - const fn first_section(&self) -> *const u8 { - &(self.sections[0]) as *const _ - } } impl TagTrait for ElfSectionsTag { @@ -75,23 +73,24 @@ impl Debug for ElfSectionsTag { /// An iterator over some ELF sections. #[derive(Clone)] -/// TODO make this memory safe with lifetime capture. -pub struct ElfSectionIter { +pub struct ElfSectionIter<'a> { current_section: *const u8, remaining_sections: u32, entry_size: u32, string_section: *const u8, + _phantom_data: PhantomData<&'a ()>, } -impl Iterator for ElfSectionIter { - type Item = ElfSection; +impl<'a> Iterator for ElfSectionIter<'a> { + type Item = ElfSection<'a>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option> { while self.remaining_sections != 0 { let section = ElfSection { inner: self.current_section, string_section: self.string_section, entry_size: self.entry_size, + _phantom: PhantomData::default(), }; self.current_section = unsafe { self.current_section.offset(self.entry_size as isize) }; @@ -105,7 +104,7 @@ impl Iterator for ElfSectionIter { } } -impl Debug for ElfSectionIter { +impl<'a> Debug for ElfSectionIter<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let mut debug = f.debug_list(); self.clone().for_each(|ref e| { @@ -115,23 +114,13 @@ impl Debug for ElfSectionIter { } } -impl Default for ElfSectionIter { - fn default() -> Self { - Self { - current_section: core::ptr::null(), - remaining_sections: 0, - entry_size: 0, - string_section: core::ptr::null(), - } - } -} - /// A single generic ELF Section. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ElfSection { +pub struct ElfSection<'a> { inner: *const u8, string_section: *const u8, entry_size: u32, + _phantom: PhantomData<&'a ()>, } #[derive(Clone, Copy, Debug)] @@ -164,7 +153,7 @@ struct ElfSectionInner64 { entry_size: u64, } -impl ElfSection { +impl<'a> ElfSection<'a> { /// Get the section type as a `ElfSectionType` enum variant. #[must_use] pub fn section_type(&self) -> ElfSectionType { diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 555fd833..0b2007f2 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -537,8 +537,6 @@ mod tests { /// Tests to parse a MBI that was statically extracted from a test run with /// GRUB as bootloader. #[test] - // TODO fix Miri - #[cfg_attr(miri, ignore)] fn grub2() { let mut bytes = AlignedBytes([ 192, 3, 0, 0, // total_size @@ -944,8 +942,6 @@ mod tests { } #[test] - // TODO fix Miri - #[cfg_attr(miri, ignore)] fn elf_sections() { let mut bytes = AlignedBytes([ 168, 0, 0, 0, // total_size From fb6c12b0d36040175d38ccb3b399d37407074d96 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:01 +0200 Subject: [PATCH 24/35] multiboot2: fix all warnings of rustc and clippy --- multiboot2/src/builder/information.rs | 73 +++++++++++++-------------- multiboot2/src/command_line.rs | 2 +- multiboot2/src/elf_sections.rs | 8 +-- multiboot2/src/lib.rs | 6 ++- multiboot2/src/memory_map.rs | 4 +- multiboot2/src/module.rs | 2 +- multiboot2/src/tag.rs | 16 +++--- multiboot2/src/test_util.rs | 2 +- multiboot2/src/util.rs | 21 +++++--- 9 files changed, 69 insertions(+), 65 deletions(-) diff --git a/multiboot2/src/builder/information.rs b/multiboot2/src/builder/information.rs index 5b317bf4..6806de8f 100644 --- a/multiboot2/src/builder/information.rs +++ b/multiboot2/src/builder/information.rs @@ -7,7 +7,6 @@ use crate::{ EFISdt32Tag, EFISdt64Tag, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, ALIGNMENT, }; -use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; use core::mem::size_of; @@ -200,32 +199,32 @@ impl InformationBuilder { /// Adds a 'basic memory information' tag (represented by [`BasicMemoryInfoTag`]) to the builder. #[must_use] - pub fn basic_memory_info_tag(self, tag: BasicMemoryInfoTag) -> Self { - self.add_tag(&tag).unwrap() + pub fn basic_memory_info_tag(self, tag: &BasicMemoryInfoTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'bootloader name' tag (represented by [`BootLoaderNameTag`]) to the builder. #[must_use] - pub fn bootloader_name_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn bootloader_name_tag(self, tag: &BootLoaderNameTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'command line' tag (represented by [`CommandLineTag`]) to the builder. #[must_use] - pub fn command_line_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn command_line_tag(self, tag: &CommandLineTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'EFI 32-bit system table pointer' tag (represented by [`EFISdt32Tag`]) to the builder. #[must_use] - pub fn efisdt32_tag(self, tag: EFISdt32Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn efisdt32_tag(self, tag: &EFISdt32Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'EFI 64-bit system table pointer' tag (represented by [`EFISdt64Tag`]) to the builder. #[must_use] - pub fn efisdt64_tag(self, tag: EFISdt64Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn efisdt64_tag(self, tag: &EFISdt64Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'EFI boot services not terminated' tag (represented by [`EFIBootServicesNotExitedTag`]) to the builder. @@ -236,69 +235,69 @@ impl InformationBuilder { /// Adds a 'EFI 32-bit image handle pointer' tag (represented by [`EFIImageHandle32Tag`]) to the builder. #[must_use] - pub fn efi_image_handle32(self, tag: EFIImageHandle32Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn efi_image_handle32(self, tag: &EFIImageHandle32Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'EFI 64-bit image handle pointer' tag (represented by [`EFIImageHandle64Tag`]) to the builder. #[must_use] - pub fn efi_image_handle64(self, tag: EFIImageHandle64Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn efi_image_handle64(self, tag: &EFIImageHandle64Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'EFI Memory map' tag (represented by [`EFIMemoryMapTag`]) to the builder. #[must_use] - pub fn efi_memory_map_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn efi_memory_map_tag(self, tag: &EFIMemoryMapTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'ELF-Symbols' tag (represented by [`ElfSectionsTag`]) to the builder. #[must_use] - pub fn elf_sections_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn elf_sections_tag(self, tag: &ElfSectionsTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'Framebuffer info' tag (represented by [`FramebufferTag`]) to the builder. #[must_use] - pub fn framebuffer_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn framebuffer_tag(self, tag: &FramebufferTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'Image load base physical address' tag (represented by [`ImageLoadPhysAddrTag`]) to the builder. #[must_use] - pub fn image_load_addr(self, tag: ImageLoadPhysAddrTag) -> Self { - self.add_tag(&tag).unwrap() + pub fn image_load_addr(self, tag: &ImageLoadPhysAddrTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a (*none EFI*) 'memory map' tag (represented by [`MemoryMapTag`]) to the builder. #[must_use] - pub fn memory_map_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn memory_map_tag(self, tag: &MemoryMapTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'Modules' tag (represented by [`ModuleTag`]) to the builder. /// This tag can occur multiple times in boot information. #[must_use] - pub fn add_module_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn add_module_tag(self, tag: &ModuleTag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'ACPI old RSDP' tag (represented by [`RsdpV1Tag`]) to the builder. #[must_use] - pub fn rsdp_v1_tag(self, tag: RsdpV1Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn rsdp_v1_tag(self, tag: &RsdpV1Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'ACPI new RSDP' tag (represented by [`RsdpV2Tag`]) to the builder. #[must_use] - pub fn rsdp_v2_tag(self, tag: RsdpV2Tag) -> Self { - self.add_tag(&tag).unwrap() + pub fn rsdp_v2_tag(self, tag: &RsdpV2Tag) -> Self { + self.add_tag(tag).unwrap() } /// Adds a 'SMBIOS tables' tag (represented by [`SmbiosTag`]) to the builder. #[must_use] - pub fn smbios_tag(self, tag: Box) -> Self { - self.add_tag(&*tag).unwrap() + pub fn smbios_tag(self, tag: &SmbiosTag) -> Self { + self.add_tag(tag).unwrap() } const fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool { @@ -322,18 +321,18 @@ mod tests { assert_eq!(builder.expected_len(), expected_len); // the most simple tag - builder = builder.basic_memory_info_tag(BasicMemoryInfoTag::new(640, 7 * 1024)); + builder = builder.basic_memory_info_tag(&BasicMemoryInfoTag::new(640, 7 * 1024)); expected_len += 16; assert_eq!(builder.expected_len(), expected_len); // a tag that has a dynamic size - builder = builder.command_line_tag(CommandLineTag::new("test")); + builder = builder.command_line_tag(&CommandLineTag::new("test")); expected_len += 8 + 5 + 3; // padding assert_eq!(builder.expected_len(), expected_len); // many modules - builder = builder.add_module_tag(ModuleTag::new(0, 1234, "module1")); + builder = builder.add_module_tag(&ModuleTag::new(0, 1234, "module1")); expected_len += 16 + 8; assert_eq!(builder.expected_len(), expected_len); - builder = builder.add_module_tag(ModuleTag::new(5678, 6789, "module2")); + builder = builder.add_module_tag(&ModuleTag::new(5678, 6789, "module2")); expected_len += 16 + 8; assert_eq!(builder.expected_len(), expected_len); diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 7cbfd06f..fedcc06f 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -6,7 +6,7 @@ use core::fmt::{Debug, Formatter}; use core::mem; use core::str; #[cfg(feature = "builder")] -use {crate::new_boxed, alloc::boxed::Box, alloc::vec::Vec}; +use {crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::(); diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 27c4c3c1..842da777 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -2,7 +2,7 @@ use crate::{TagHeader, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; -use core::marker::{PhantomData, PhantomPinned}; +use core::marker::PhantomData; use core::mem; use core::str::Utf8Error; #[cfg(feature = "builder")] @@ -35,7 +35,7 @@ impl ElfSectionsTag { } /// Get an iterator of loaded ELF sections. - pub(crate) fn sections(&self) -> ElfSectionIter { + pub(crate) 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 _ }; @@ -44,7 +44,7 @@ impl ElfSectionsTag { remaining_sections: self.number_of_sections, entry_size: self.entry_size, string_section: string_section_ptr, - _phantom_data: PhantomData::default(), + _phantom_data: PhantomData, } } } @@ -90,7 +90,7 @@ impl<'a> Iterator for ElfSectionIter<'a> { inner: self.current_section, string_section: self.string_section, entry_size: self.entry_size, - _phantom: PhantomData::default(), + _phantom: PhantomData, }; self.current_section = unsafe { self.current_section.offset(self.entry_size as isize) }; diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 0b2007f2..96d4adf2 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -12,7 +12,7 @@ // now allow a few rules which are denied by the above statement // --> They are either ridiculous, not necessary, or we can't fix them. #![allow(clippy::multiple_crate_versions)] -// #![deny(missing_docs)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rustdoc::all)] // --- END STYLE CHECKS --- @@ -100,7 +100,9 @@ pub use smbios::SmbiosTag; pub use tag::TagHeader; pub use tag_trait::TagTrait; pub use tag_type::{TagType, TagTypeId}; -pub use util::{new_boxed, parse_slice_as_string, StringError}; +#[cfg(feature = "alloc")] +pub use util::new_boxed; +pub use util::{parse_slice_as_string, StringError}; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index 3f056c92..c7663d72 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -43,7 +43,7 @@ impl MemoryMapTag { let entry_version = 0_u32.to_ne_bytes(); let areas = { let ptr = areas.as_ptr().cast::(); - let len = areas.len() * size_of::(); + let len = mem::size_of_val(areas); unsafe { slice::from_raw_parts(ptr, len) } }; new_boxed(&[&entry_size, &entry_version, areas]) @@ -322,7 +322,7 @@ impl EFIMemoryMapTag { pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box { let efi_mmap = { let ptr = descs.as_ptr().cast::(); - let len = descs.len() * size_of::(); + let len = mem::size_of_val(descs); unsafe { slice::from_raw_parts(ptr, len) } }; diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index d36ee549..c3c79aa5 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -5,7 +5,7 @@ use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; #[cfg(feature = "builder")] -use {crate::new_boxed, alloc::boxed::Box, alloc::vec::Vec}; +use {crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::() + 2 * mem::size_of::(); diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 3336f145..24242c02 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -57,7 +57,7 @@ impl TagHeader { /// is [`ALIGNMENT`]-aligned #[derive(Clone, Debug, PartialEq, Eq)] #[repr(transparent)] -pub(crate) struct TagBytesRef<'a>(&'a [u8]); +pub struct TagBytesRef<'a>(&'a [u8]); impl<'a> TryFrom<&'a [u8]> for TagBytesRef<'a> { type Error = MemoryError; @@ -124,18 +124,16 @@ impl GenericTag { let dst_len = Self::dst_len(header); assert_eq!(header.size as usize, Self::BASE_SIZE + dst_len); - let generic_tag: *const GenericTag = - ptr_meta::from_raw_parts(bytes.as_ptr().cast(), dst_len); - let generic_tag = unsafe { &*generic_tag }; - - generic_tag + let generic_tag: *const Self = ptr_meta::from_raw_parts(bytes.as_ptr().cast(), dst_len); + unsafe { &*generic_tag } } - pub fn header(&self) -> &TagHeader { + pub const fn header(&self) -> &TagHeader { &self.header } - pub fn payload(&self) -> &[u8] { + #[cfg(all(test, feature = "builder"))] + pub const fn payload(&self) -> &[u8] { &self.payload } @@ -346,7 +344,7 @@ mod tests { // Guaranteed wrong alignment let unaligned_slice = &slice[3..]; assert_eq!( - TagBytesRef::try_from(&unaligned_slice[..]), + TagBytesRef::try_from(unaligned_slice), Err(MemoryError::WrongAlignment) ); diff --git a/multiboot2/src/test_util.rs b/multiboot2/src/test_util.rs index a56156d0..9bf466e8 100644 --- a/multiboot2/src/test_util.rs +++ b/multiboot2/src/test_util.rs @@ -9,7 +9,7 @@ use core::ops::Deref; /// information or just the bytes for simple tags, in a manual and raw approach. #[cfg(test)] #[repr(C, align(8))] -pub(crate) struct AlignedBytes(pub [u8; N]); +pub struct AlignedBytes(pub [u8; N]); impl AlignedBytes { pub const fn new(bytes: [u8; N]) -> Self { diff --git a/multiboot2/src/util.rs b/multiboot2/src/util.rs index 8cdaa671..c9ed3fa6 100644 --- a/multiboot2/src/util.rs +++ b/multiboot2/src/util.rs @@ -1,11 +1,14 @@ //! Various utilities. -use crate::tag::GenericTag; -use crate::{TagHeader, TagTrait, TagType, ALIGNMENT}; +use crate::ALIGNMENT; use core::fmt; use core::fmt::{Display, Formatter}; use core::str::Utf8Error; -use core::{ptr, slice}; +#[cfg(feature = "alloc")] +use { + crate::{TagHeader, TagTrait}, + core::{mem, ptr}, +}; #[cfg(feature = "builder")] use {alloc::alloc::Layout, alloc::boxed::Box}; @@ -45,13 +48,14 @@ impl core::error::Error for StringError { /// without additional padding in-between. You don't need to add the bytes /// for [`TagHeader`], but only additional ones. #[cfg(feature = "alloc")] +#[must_use] pub fn new_boxed(additional_bytes_slices: &[&[u8]]) -> Box { let additional_size = additional_bytes_slices .iter() .map(|b| b.len()) .sum::(); - let size = size_of::() + additional_size; + let size = mem::size_of::() + additional_size; let alloc_size = increase_to_alignment(size); let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap(); let heap_ptr = unsafe { alloc::alloc::alloc(layout) }; @@ -62,7 +66,7 @@ pub fn new_boxed(additional_bytes_slices: &[&[u8]]) -> Box heap_ptr.cast::().add(1).write(size as u32); } - let mut write_offset = size_of::(); + let mut write_offset = mem::size_of::(); for &bytes in additional_bytes_slices { unsafe { let len = bytes.len(); @@ -101,8 +105,8 @@ pub const fn increase_to_alignment(size: usize) -> usize { #[cfg(test)] mod tests { use super::*; - use crate::tag::GenericTag; - use crate::CommandLineTag; + #[cfg(feature = "alloc")] + use {crate::tag::GenericTag, crate::CommandLineTag}; #[test] fn test_parse_slice_as_string() { @@ -140,6 +144,7 @@ mod tests { } #[test] + #[cfg(feature = "alloc")] fn test_new_boxed() { let tag = new_boxed::(&[&[0, 1, 2, 3]]); assert_eq!(tag.header().typ, GenericTag::ID); @@ -150,7 +155,7 @@ mod tests { assert_eq!(tag.header().typ, GenericTag::ID); assert_eq!(tag.payload(), &[0, 1, 2, 3]); - let tag = new_boxed::(&["hello\0".as_bytes()]); + let tag = new_boxed::(&[b"hello\0"]); assert_eq!(tag.cmdline(), Ok("hello")); } } From 41cbe18ecf749d0ba9adbfa074e54d82a4d2cc06 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:00:16 +0200 Subject: [PATCH 25/35] multiboot2: tiny code improvement --- .../bins/multiboot2_chainloader/src/loader.rs | 8 ++++---- multiboot2/src/boot_loader_name.rs | 3 ++- multiboot2/src/command_line.rs | 3 ++- multiboot2/src/module.rs | 3 ++- multiboot2/src/smbios.rs | 3 ++- multiboot2/src/tag.rs | 13 +++++++------ multiboot2/src/test_util.rs | 2 +- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/integration-test/bins/multiboot2_chainloader/src/loader.rs b/integration-test/bins/multiboot2_chainloader/src/loader.rs index 896cf2af..3668595e 100644 --- a/integration-test/bins/multiboot2_chainloader/src/loader.rs +++ b/integration-test/bins/multiboot2_chainloader/src/loader.rs @@ -44,15 +44,15 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! { // build MBI let mbi = multiboot2::builder::InformationBuilder::new() - .bootloader_name_tag(BootLoaderNameTag::new("mb2_integrationtest_chainloader")) - .command_line_tag(CommandLineTag::new("chainloaded YEAH")) + .bootloader_name_tag(&BootLoaderNameTag::new("mb2_integrationtest_chainloader")) + .command_line_tag(&CommandLineTag::new("chainloaded YEAH")) // random non-sense memory map - .memory_map_tag(MemoryMapTag::new(&[MemoryArea::new( + .memory_map_tag(&MemoryMapTag::new(&[MemoryArea::new( 0, 0xffffffff, MemoryAreaType::Reserved, )])) - .add_module_tag(ModuleTag::new( + .add_module_tag(&ModuleTag::new( elf_mod.start as u32, elf_mod.end as u32, elf_mod.string.unwrap(), diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 3fc1810a..32db2cfc 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -89,6 +89,7 @@ mod tests { use super::*; use crate::tag::{GenericTag, TagBytesRef}; use crate::test_util::AlignedBytes; + use core::borrow::Borrow; #[rustfmt::skip] fn get_bytes() -> AlignedBytes<16> { @@ -105,7 +106,7 @@ mod tests { #[test] fn test_parse_str() { let bytes = get_bytes(); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::BootLoaderName); diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index fedcc06f..cd387f25 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -83,6 +83,7 @@ mod tests { use super::*; use crate::tag::{GenericTag, TagBytesRef}; use crate::test_util::AlignedBytes; + use core::borrow::Borrow; #[rustfmt::skip] fn get_bytes() -> AlignedBytes<16> { @@ -99,7 +100,7 @@ mod tests { #[test] fn test_parse_str() { let bytes = get_bytes(); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Cmdline); diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index c3c79aa5..c77eb7e7 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -130,6 +130,7 @@ mod tests { use super::*; use crate::tag::{GenericTag, TagBytesRef}; use crate::test_util::AlignedBytes; + use core::borrow::Borrow; #[rustfmt::skip] fn get_bytes() -> AlignedBytes<24> { @@ -150,7 +151,7 @@ mod tests { #[test] fn test_parse_str() { let bytes = get_bytes(); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Module); diff --git a/multiboot2/src/smbios.rs b/multiboot2/src/smbios.rs index 208268ae..abd0e842 100644 --- a/multiboot2/src/smbios.rs +++ b/multiboot2/src/smbios.rs @@ -73,6 +73,7 @@ mod tests { use super::*; use crate::tag::{GenericTag, TagBytesRef}; use crate::test_util::AlignedBytes; + use core::borrow::Borrow; #[rustfmt::skip] fn get_bytes() -> AlignedBytes<32> { @@ -96,7 +97,7 @@ mod tests { #[test] fn test_parse() { let bytes = get_bytes(); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let tag = tag.cast::(); assert_eq!(tag.header.typ, TagType::Smbios); diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 24242c02..8463e1dd 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -236,6 +236,7 @@ impl<'a> Iterator for TagIter<'a> { mod tests { use super::*; use crate::test_util::AlignedBytes; + use core::borrow::Borrow; use core::mem; #[test] @@ -248,7 +249,7 @@ mod tests { 0x13, 0x37, 0x13, 0x37, ]); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); assert_eq!(tag.header.typ, 0xffff_ffff); assert_eq!(tag.header.size, 16); @@ -277,7 +278,7 @@ mod tests { 0xef, 0xbe, 0xad, 0xde, /* field b: 0x1337_1337 */ 0x37, 0x13, 0x37, 0x13, ]); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let custom_tag = tag.cast::(); @@ -313,7 +314,7 @@ mod tests { 0x37, 0x13, 0x37, 0x13, ]); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); let custom_tag = tag.cast::(); @@ -369,7 +370,7 @@ mod tests { 0, 0, 0, 0, 0, 0 ], ); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); assert_eq!(tag.header.typ, TagType::Cmdline); assert_eq!(tag.header.size, 8 + 10); @@ -391,7 +392,7 @@ mod tests { 0, 0, 0, 0, 0, 0 ], ); - let bytes = TagBytesRef::try_from(&bytes[..]).unwrap(); + let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap(); let tag = GenericTag::ref_from(bytes); // Main objective here is also that this test passes Miri. @@ -419,7 +420,7 @@ mod tests { 8, 0, 0, 0, ], ); - let mut iter = TagIter::new(&bytes[..]); + let mut iter = TagIter::new(bytes.borrow()); let first = iter.next().unwrap(); assert_eq!(first.header.typ, TagType::Custom(0xff)); assert_eq!(first.header.size, 8); diff --git a/multiboot2/src/test_util.rs b/multiboot2/src/test_util.rs index 9bf466e8..2a983dc9 100644 --- a/multiboot2/src/test_util.rs +++ b/multiboot2/src/test_util.rs @@ -82,6 +82,6 @@ mod tests { 0, 0, ] ); - let _a = TagBytesRef::try_from(&bytes[..]).unwrap(); + let _a = TagBytesRef::try_from(bytes.borrow()).unwrap(); } } From 8d39ec8ef1782b401c3a534713087f490bc24c39 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:02:20 +0200 Subject: [PATCH 26/35] multiboot2: doc fixes --- multiboot2/src/boot_information.rs | 4 +++- multiboot2/src/tag.rs | 3 ++- multiboot2/src/tag_type.rs | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 68863af8..6ee085d6 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -8,7 +8,7 @@ use crate::{ module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag, - ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag, + ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, VBEInfoTag, }; use core::fmt; use core::mem; @@ -221,6 +221,8 @@ impl<'a> BootInformation<'a> { /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None` /// as it is strictly recommended to get the memory map from the `uefi` /// services. + /// + /// [`TagType::EfiBs`]: crate::TagType::EfiBs #[must_use] pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> { // If the EFIBootServicesNotExited is present, then we should not use diff --git a/multiboot2/src/tag.rs b/multiboot2/src/tag.rs index 8463e1dd..3943456d 100644 --- a/multiboot2/src/tag.rs +++ b/multiboot2/src/tag.rs @@ -17,10 +17,11 @@ use core::ops::Deref; use core::ptr; /// The common header that all tags have in common. This type is ABI compatible. -/// It is the sized counterpart of [`GenericTag`]. /// /// Not to be confused with Multiboot header tags, which are something /// different. +/// +/// It is the sized counterpart of `GenericTag`, an internal type. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[repr(C, align(8))] // Alignment also propagates to all tag types using this. pub struct TagHeader { diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 9c47c089..f7ae50df 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -7,10 +7,10 @@ use core::hash::Hash; /// Serialized form of [`TagType`] that matches the binary representation /// (`u32`). The abstraction corresponds to the `typ`/`type` field of a -/// Multiboot2 [`Tag`]. This type can easily be created from or converted to +/// Multiboot2 [`TagHeader`]. This type can easily be created from or converted to /// [`TagType`]. /// -/// [`Tag`]: crate::Tag +/// [`TagHeader`]: crate::TagHeader #[repr(transparent)] #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)] pub struct TagTypeId(u32); From 4edcf47fde55aa030b70cd4bd7481b3f91f8c8f9 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:38:13 +0200 Subject: [PATCH 27/35] multiboot2: streamline tags - all public fields -> methods - Added missing InformationBuilder support for VBEInfoTag --- multiboot2/src/boot_information.rs | 8 ++- multiboot2/src/boot_loader_name.rs | 6 +- multiboot2/src/builder/information.rs | 10 +++- multiboot2/src/elf_sections.rs | 27 +++++++-- multiboot2/src/lib.rs | 74 ++++++++++++------------- multiboot2/src/vbe_info.rs | 80 +++++++++++++++++++++------ 6 files changed, 139 insertions(+), 66 deletions(-) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 6ee085d6..9cd2485e 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -8,7 +8,7 @@ use crate::{ module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag, - ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, VBEInfoTag, + ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag, }; use core::fmt; use core::mem; @@ -279,8 +279,8 @@ impl<'a> BootInformation<'a> { pub fn elf_sections(&self) -> Option { let tag = self.get_tag::(); tag.map(|t| { - assert!((t.entry_size * t.shndx) <= t.size() as u32); - t.sections() + assert!((t.entry_size() * t.shndx()) <= t.size() as u32); + t.sections_iter() }) } @@ -397,6 +397,8 @@ impl<'a> BootInformation<'a> { /// .unwrap(); /// assert_eq!(tag.name(), Ok("name")); /// ``` + /// + /// [`TagType`]: crate::TagType #[must_use] pub fn get_tag(&'a self) -> Option<&'a TagT> { self.tags() diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index 32db2cfc..60a2cc74 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,11 +1,11 @@ //! Module for [`BootLoaderNameTag`]. use crate::tag::TagHeader; -use crate::{new_boxed, parse_slice_as_string, StringError, TagTrait, TagType}; -#[cfg(feature = "builder")] -use alloc::boxed::Box; +use crate::{parse_slice_as_string, StringError, TagTrait, TagType}; use core::fmt::{Debug, Formatter}; use core::mem; +#[cfg(feature = "builder")] +use {crate::new_boxed, alloc::boxed::Box}; const METADATA_SIZE: usize = mem::size_of::(); diff --git a/multiboot2/src/builder/information.rs b/multiboot2/src/builder/information.rs index 6806de8f..a52a48da 100644 --- a/multiboot2/src/builder/information.rs +++ b/multiboot2/src/builder/information.rs @@ -5,7 +5,8 @@ use crate::{ BasicMemoryInfoTag, BootInformationHeader, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, - MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, ALIGNMENT, + MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, TagType, VBEInfoTag, + ALIGNMENT, }; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; @@ -300,6 +301,13 @@ impl InformationBuilder { self.add_tag(tag).unwrap() } + /// Adds a 'VBE Info' tag (represented by [`VBEInfoTag`]) to the builder. + #[must_use] + pub fn vbe_info_tag(self, tag: &VBEInfoTag) -> Self { + self.add_tag(tag).unwrap() + } + + #[must_use] const fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool { matches!( tag_type, diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index 842da777..decbe734 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -18,8 +18,8 @@ const METADATA_SIZE: usize = mem::size_of::() + 3 * mem::size_of:: ElfSectionIter { + #[must_use] + pub(crate) const fn sections_iter(&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 _ }; @@ -47,6 +48,24 @@ impl ElfSectionsTag { _phantom_data: PhantomData, } } + + /// Returns the amount of sections. + #[must_use] + pub const fn number_of_sections(&self) -> u32 { + self.number_of_sections + } + + /// Returns the size of each entry. + #[must_use] + pub const fn entry_size(&self) -> u32 { + self.entry_size + } + + /// Returns the index of the section header string table. + #[must_use] + pub const fn shndx(&self) -> u32 { + self.shndx + } } impl TagTrait for ElfSectionsTag { @@ -66,7 +85,7 @@ 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()) + .field("sections", &self.sections_iter()) .finish() } } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 96d4adf2..90f6b6c6 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -448,83 +448,83 @@ mod tests { let vbe = bi.vbe_info_tag().unwrap(); use vbe_info::*; - assert_eq!({ vbe.mode }, 16762); - assert_eq!({ vbe.interface_segment }, 65535); - assert_eq!({ vbe.interface_offset }, 24576); - assert_eq!({ vbe.interface_length }, 79); - assert_eq!({ vbe.control_info.signature }, [86, 69, 83, 65]); - assert_eq!({ vbe.control_info.version }, 768); - assert_eq!({ vbe.control_info.oem_string_ptr }, 3221247964); + assert_eq!({ vbe.mode() }, 16762); + assert_eq!({ vbe.interface_segment() }, 65535); + assert_eq!({ vbe.interface_offset() }, 24576); + assert_eq!({ vbe.interface_length() }, 79); + assert_eq!({ vbe.control_info().signature }, [86, 69, 83, 65]); + assert_eq!({ vbe.control_info().version }, 768); + assert_eq!({ vbe.control_info().oem_string_ptr }, 3221247964); assert_eq!( - { vbe.control_info.capabilities }, + { vbe.control_info().capabilities }, VBECapabilities::SWITCHABLE_DAC ); - assert_eq!({ vbe.control_info.mode_list_ptr }, 1610645538); - assert_eq!({ vbe.control_info.total_memory }, 256); - assert_eq!({ vbe.control_info.oem_software_revision }, 0); - assert_eq!({ vbe.control_info.oem_vendor_name_ptr }, 3221247984); - assert_eq!({ vbe.control_info.oem_product_name_ptr }, 3221248003); - assert_eq!({ vbe.control_info.oem_product_revision_ptr }, 3221248023); - assert!({ vbe.mode_info.mode_attributes }.contains( + assert_eq!({ vbe.control_info().mode_list_ptr }, 1610645538); + assert_eq!({ vbe.control_info().total_memory }, 256); + assert_eq!({ vbe.control_info().oem_software_revision }, 0); + assert_eq!({ vbe.control_info().oem_vendor_name_ptr }, 3221247984); + assert_eq!({ vbe.control_info().oem_product_name_ptr }, 3221248003); + assert_eq!({ vbe.control_info().oem_product_revision_ptr }, 3221248023); + assert!({ vbe.mode_info().mode_attributes }.contains( VBEModeAttributes::SUPPORTED | VBEModeAttributes::COLOR | VBEModeAttributes::GRAPHICS | VBEModeAttributes::NOT_VGA_COMPATIBLE | VBEModeAttributes::LINEAR_FRAMEBUFFER )); - assert!(vbe.mode_info.window_a_attributes.contains( + assert!(vbe.mode_info().window_a_attributes.contains( VBEWindowAttributes::RELOCATABLE | VBEWindowAttributes::READABLE | VBEWindowAttributes::WRITEABLE )); - assert_eq!({ vbe.mode_info.window_granularity }, 64); - assert_eq!({ vbe.mode_info.window_size }, 64); - assert_eq!({ vbe.mode_info.window_a_segment }, 40960); - assert_eq!({ vbe.mode_info.window_function_ptr }, 3221247162); - assert_eq!({ vbe.mode_info.pitch }, 5120); - assert_eq!({ vbe.mode_info.resolution }, (1280, 800)); - assert_eq!(vbe.mode_info.character_size, (8, 16)); - assert_eq!(vbe.mode_info.number_of_planes, 1); - assert_eq!(vbe.mode_info.bpp, 32); - assert_eq!(vbe.mode_info.number_of_banks, 1); - assert_eq!(vbe.mode_info.memory_model, VBEMemoryModel::DirectColor); - assert_eq!(vbe.mode_info.bank_size, 0); - assert_eq!(vbe.mode_info.number_of_image_pages, 3); + assert_eq!({ vbe.mode_info().window_granularity }, 64); + assert_eq!({ vbe.mode_info().window_size }, 64); + assert_eq!({ vbe.mode_info().window_a_segment }, 40960); + assert_eq!({ vbe.mode_info().window_function_ptr }, 3221247162); + assert_eq!({ vbe.mode_info().pitch }, 5120); + assert_eq!({ vbe.mode_info().resolution }, (1280, 800)); + assert_eq!(vbe.mode_info().character_size, (8, 16)); + assert_eq!(vbe.mode_info().number_of_planes, 1); + assert_eq!(vbe.mode_info().bpp, 32); + assert_eq!(vbe.mode_info().number_of_banks, 1); + assert_eq!(vbe.mode_info().memory_model, VBEMemoryModel::DirectColor); + assert_eq!(vbe.mode_info().bank_size, 0); + assert_eq!(vbe.mode_info().number_of_image_pages, 3); assert_eq!( - vbe.mode_info.red_field, + vbe.mode_info().red_field, VBEField { position: 16, size: 8, } ); assert_eq!( - vbe.mode_info.green_field, + vbe.mode_info().green_field, VBEField { position: 8, size: 8, } ); assert_eq!( - vbe.mode_info.blue_field, + vbe.mode_info().blue_field, VBEField { position: 0, size: 8, } ); assert_eq!( - vbe.mode_info.reserved_field, + vbe.mode_info().reserved_field, VBEField { position: 24, size: 8, } ); assert_eq!( - vbe.mode_info.direct_color_attributes, + vbe.mode_info().direct_color_attributes, VBEDirectColorAttributes::RESERVED_USABLE ); - assert_eq!({ vbe.mode_info.framebuffer_base_ptr }, 4244635648); - assert_eq!({ vbe.mode_info.offscreen_memory_offset }, 0); - assert_eq!({ vbe.mode_info.offscreen_memory_size }, 0); + assert_eq!({ vbe.mode_info().framebuffer_base_ptr }, 4244635648); + assert_eq!({ vbe.mode_info().offscreen_memory_offset }, 0); + assert_eq!({ vbe.mode_info().offscreen_memory_size }, 0); } #[test] diff --git a/multiboot2/src/vbe_info.rs b/multiboot2/src/vbe_info.rs index 961cc883..8670c0ae 100644 --- a/multiboot2/src/vbe_info.rs +++ b/multiboot2/src/vbe_info.rs @@ -1,42 +1,86 @@ //! Module for [`VBEInfoTag`]. -use crate::{TagHeader, TagTrait, TagType, TagTypeId}; +use crate::{TagHeader, TagTrait, TagType}; use core::fmt; +use core::mem; /// This tag contains VBE metadata, VBE controller information returned by the /// VBE Function 00h and VBE mode information returned by the VBE Function 01h. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C, align(8))] pub struct VBEInfoTag { - typ: TagTypeId, - length: u32, + header: TagHeader, + mode: u16, + interface_segment: u16, + interface_offset: u16, + interface_length: u16, + control_info: VBEControlInfo, + mode_info: VBEModeInfo, +} + +impl VBEInfoTag { + /// Constructs a new tag. + #[cfg(feature = "builder")] + #[must_use] + pub fn new( + mode: u16, + interface_segment: u16, + interface_offset: u16, + interface_length: u16, + control_info: VBEControlInfo, + mode_info: VBEModeInfo, + ) -> Self { + Self { + header: TagHeader::new(Self::ID, mem::size_of::().try_into().unwrap()), + mode, + interface_segment, + interface_offset, + interface_length, + control_info, + mode_info, + } + } /// Indicates current video mode in the format specified in VBE 3.0. - pub mode: u16, + #[must_use] + pub const fn mode(&self) -> u16 { + self.mode + } - /// Contain the segment of the table of a protected mode interface defined in VBE 2.0+. + /// Returns the segment of the table of a protected mode interface defined in VBE 2.0+. /// /// If the information for a protected mode interface is not available /// this field is set to zero. - pub interface_segment: u16, - - /// Contain the segment offset of the table of a protected mode interface defined in VBE 2.0+. + #[must_use] + pub const fn interface_segment(&self) -> u16 { + self.interface_segment + } + /// Returns the segment offset of the table of a protected mode interface defined in VBE 2.0+. /// /// If the information for a protected mode interface is not available /// this field is set to zero. - pub interface_offset: u16, - - /// Contain the segment length of the table of a protected mode interface defined in VBE 2.0+. + #[must_use] + pub const fn interface_offset(&self) -> u16 { + self.interface_offset + } + /// Returns the segment length of the table of a protected mode interface defined in VBE 2.0+. /// /// If the information for a protected mode interface is not available /// this field is set to zero. - pub interface_length: u16, - - /// Contains VBE controller information returned by the VBE Function `00h`. - pub control_info: VBEControlInfo, - - /// Contains VBE mode information returned by the VBE Function `01h`. - pub mode_info: VBEModeInfo, + #[must_use] + pub const fn interface_length(&self) -> u16 { + self.interface_length + } + /// Returns VBE controller information returned by the VBE Function `00h`. + #[must_use] + pub const fn control_info(&self) -> VBEControlInfo { + self.control_info + } + /// Returns VBE mode information returned by the VBE Function `01h`. + #[must_use] + pub const fn mode_info(&self) -> VBEModeInfo { + self.mode_info + } } impl TagTrait for VBEInfoTag { From 4f93c1c54a5a052777e74fac112c5fbc280b4338 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:48:24 +0200 Subject: [PATCH 28/35] doc: update changelog --- multiboot2/Changelog.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 3951b01d..bc377e33 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -2,16 +2,34 @@ ## Unreleased -- **Breaking** All functions that returns something useful are now `#[must_use]` -- **Breaking** More public fields in tags were replaced by public getters, such +## 0.21.0 (2024-08-17) + +This release contains a massive refactoring of various internals. Now, **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 provide external custom tags, you +should be fine. + +Please note that **all previous releases** must be considered unsafe, as they +contain UB. However, it is never clear how UB results in immediate incorrect +behaviour and it _might_ work. **Nevertheless, please migrate to the latest +release and you'll be fine!** + +- **Breaking:** All functions that returns something useful are + now `#[must_use]` +- **Breaking:** More public fields in tags were replaced by public getters, such as `SmbiosTag::major()` -- **BREAKING:** `multiboot2::{StringError};` -> \ - `multiboot2::util::{StringError};` -- updated dependencies -- MSRV is 1.75 +- **Breaking:** Methods of `InformationBuilder` to add tags now consume + references instead of owned values +- **Breaking:** The `BoxedDst` has been removed in favor of a normal Rust `Box`. + This only affects you if you use the `builder` feature. +- **Breaking:** MSRV is 1.75 +- **Breaking:** Introduced new `TagHeader` type as replacement for the `Tag` + type that will be changed in the next step. `Tag` has been renamed to an + internal-only `GenericTag` type. +- Added missing `InformationBuilder::vbe_info_tag` - documentation enhancements -- Introduced new `TagHeader` type as replacement for the `Tag` type that will - be changed in the next step. +- updated dependencies ## 0.20.2 (2024-05-26) From 6c1aa358db1a0d5e10a79c879788d162a8e3aa39 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:49:29 +0200 Subject: [PATCH 29/35] Revert "msrv: update from 1.70 to 1.75" This reverts commit 484160e8 --- .github/workflows/rust.yml | 6 +++--- multiboot2-header/Cargo.toml | 2 +- multiboot2-header/Changelog.md | 2 -- multiboot2-header/README.md | 2 +- multiboot2-header/src/lib.rs | 2 +- multiboot2/Cargo.toml | 2 +- multiboot2/README.md | 2 +- multiboot2/src/lib.rs | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3c914f55..70715b72 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.70.0 # MSRV do-style-check: false features: builder @@ -50,7 +50,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.75.0 # MSRV + rust-version: 1.70.0 # MSRV do-style-check: false rust-target: thumbv7em-none-eabihf features: builder @@ -107,7 +107,7 @@ jobs: needs: build_msrv uses: ./.github/workflows/_build-rust.yml with: - rust-version: 1.75.0 # MSRV + rust-version: 1.70.0 # MSRV do-style-check: true do-test: false features: builder diff --git a/multiboot2-header/Cargo.toml b/multiboot2-header/Cargo.toml index e94b20e7..0fd73855 100644 --- a/multiboot2-header/Cargo.toml +++ b/multiboot2-header/Cargo.toml @@ -26,7 +26,7 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2-header" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2-header" -rust-version = "1.75" +rust-version = "1.70" [[example]] name = "minimal" diff --git a/multiboot2-header/Changelog.md b/multiboot2-header/Changelog.md index df9f05e8..e37f1b34 100644 --- a/multiboot2-header/Changelog.md +++ b/multiboot2-header/Changelog.md @@ -3,8 +3,6 @@ ## Unreleased - **Breaking** All functions that returns something useful are now `#[must_use]` -- updated dependencies -- documentation enhancements ## 0.4.0 (2024-05-01) diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index 577a75a8..eebac8d0 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -77,7 +77,7 @@ bytes of the ELF. See Multiboot2 specification. ## MSRV -The MSRV is 1.75.0 stable. +The MSRV is 1.70.0 stable. ## License & Contribution diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 578511e8..06b6b726 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -34,7 +34,7 @@ //! //! ## MSRV //! -//! The MSRV is 1.75.0 stable. +//! The MSRV is 1.70.0 stable. #![no_std] #![cfg_attr(feature = "unstable", feature(error_in_core))] diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index c01e8b1b..1fd7de2b 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -31,7 +31,7 @@ readme = "README.md" homepage = "https://github.com/rust-osdev/multiboot2" repository = "https://github.com/rust-osdev/multiboot2" documentation = "https://docs.rs/multiboot2" -rust-version = "1.75" +rust-version = "1.70" [features] default = ["builder"] diff --git a/multiboot2/README.md b/multiboot2/README.md index 52a207e5..4bdd1893 100644 --- a/multiboot2/README.md +++ b/multiboot2/README.md @@ -45,7 +45,7 @@ tag_, which is a tag of type `0` and size `8`. ## MSRV -The MSRV is 1.75.0 stable. +The MSRV is 1.70.0 stable. ## License & Contribution diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 90f6b6c6..6357e71f 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -41,7 +41,7 @@ //! ``` //! //! ## MSRV -//! The MSRV is 1.75.0 stable. +//! The MSRV is 1.70.0 stable. #[cfg(feature = "builder")] extern crate alloc; From 9df999fb53b8cc45af7d083d36639767775c35c9 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 11:59:21 +0200 Subject: [PATCH 30/35] workspace: downgrade derive_more to 0.99 This way, I don't have to increase the MSRV for the next release. I want as many users as possible to adopt the latest version, so I want to make it less breaking. --- Cargo.lock | 20 ++------------------ Cargo.toml | 2 +- integration-test/bins/Cargo.lock | 20 ++------------------ multiboot2/Changelog.md | 4 ++++ multiboot2/src/boot_information.rs | 6 +++--- multiboot2/src/framebuffer.rs | 2 +- 6 files changed, 13 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e378ffe..a17cb6d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,23 +10,13 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" -version = "1.0.0" +version = "0.99.18" 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" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", "syn 2.0.74", - "unicode-xid", ] [[package]] @@ -136,9 +126,3 @@ 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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" diff --git a/Cargo.toml b/Cargo.toml index e5eacb6c..59e8e1e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ exclude = [ [workspace.dependencies] bitflags = "2.6.0" -derive_more = { version = "1.0.0", default-features = false, features = ["display"] } +derive_more = { version = "~0.99.18", default-features = false, features = ["display"] } log = { version = "~0.4", default-features = false } # This way, the "multiboot2" dependency in the multiboot2-header crate can be diff --git a/integration-test/bins/Cargo.lock b/integration-test/bins/Cargo.lock index d87a36f5..ec2c77f7 100644 --- a/integration-test/bins/Cargo.lock +++ b/integration-test/bins/Cargo.lock @@ -34,23 +34,13 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "derive_more" -version = "1.0.0" +version = "0.99.18" 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" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", "syn 2.0.74", - "unicode-xid", ] [[package]] @@ -277,12 +267,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "util" version = "0.1.0" diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index bc377e33..2e3968c3 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -2,6 +2,8 @@ ## Unreleased +- + ## 0.21.0 (2024-08-17) This release contains a massive refactoring of various internals. Now, **all @@ -15,6 +17,8 @@ contain UB. However, it is never clear how UB results in immediate incorrect behaviour and it _might_ work. **Nevertheless, please migrate to the latest release and you'll be fine!** +All previous releases on crates.io have been yanked. + - **Breaking:** All functions that returns something useful are now `#[must_use]` - **Breaking:** More public fields in tags were replaced by public getters, such diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 9cd2485e..8ed11b6b 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -21,15 +21,15 @@ use derive_more::Display; pub enum MbiLoadError { /// The address is invalid. Make sure that the address is 8-byte aligned, /// according to the spec. - #[display("The address is invalid")] + #[display(fmt = "The address is invalid")] IllegalAddress, /// The total size of the multiboot2 information structure must be not zero /// and a multiple of 8. - #[display("The size of the MBI is unexpected")] + #[display(fmt = "The size of the MBI is unexpected")] IllegalTotalSize(u32), /// Missing end tag. Each multiboot2 boot information requires to have an /// end tag. - #[display("There is no end tag")] + #[display(fmt = "There is no end tag")] NoEndTag, } diff --git a/multiboot2/src/framebuffer.rs b/multiboot2/src/framebuffer.rs index 0083905a..d1370ee8 100644 --- a/multiboot2/src/framebuffer.rs +++ b/multiboot2/src/framebuffer.rs @@ -331,7 +331,7 @@ impl AsBytes for FramebufferColor {} /// Error when an unknown [`FramebufferTypeId`] is found. #[derive(Debug, Copy, Clone, Display, PartialEq, Eq)] -#[display("Unknown framebuffer type {}", _0)] +#[display(fmt = "Unknown framebuffer type {}", _0)] pub struct UnknownFramebufferType(u8); #[cfg(feature = "unstable")] From 9f23b9a2b7b4f8ed7b1694c02166995dd6342727 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 10:52:02 +0200 Subject: [PATCH 31/35] ci: run miri also with tree-borrow model --- .github/workflows/_build-rust.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_build-rust.yml b/.github/workflows/_build-rust.yml index 27c01a2f..80971d17 100644 --- a/.github/workflows/_build-rust.yml +++ b/.github/workflows/_build-rust.yml @@ -98,8 +98,9 @@ jobs: run: cargo test --verbose - name: Unit Test with Miri if: inputs.do-miri - # "--tests" so that the doctests are skipped. Currently, the doctest - # in miri fails. run: | rustup component add miri - cargo miri test --tests + # Run with stack-borrow model + cargo miri test + # Run with tree-borrow model + MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test From 3cb74b12fa027c6513a2a52d8328edef5f471b90 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 12:15:26 +0200 Subject: [PATCH 32/35] ci: temporarily, run miri only for multiboot2 The same changes made in this crate, also needs to be done in multiboot2-header. --- .github/workflows/_build-rust.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_build-rust.yml b/.github/workflows/_build-rust.yml index 80971d17..d855a822 100644 --- a/.github/workflows/_build-rust.yml +++ b/.github/workflows/_build-rust.yml @@ -101,6 +101,8 @@ jobs: run: | rustup component add miri # Run with stack-borrow model - cargo miri test + # XXX Temporarily, just for multiboot2 crate. + cargo miri test -p multiboot2 # Run with tree-borrow model - MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test + # XXX Temporarily, just for multiboot2 crate. + MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test -p multiboot2 From 3484e63022d65135802ee54ecf3b4a3d85ef2292 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 12:30:30 +0200 Subject: [PATCH 33/35] multiboot2: clippy fixes only needed for Rust 1.70 --- multiboot2-header/src/builder/header.rs | 1 + multiboot2/src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/multiboot2-header/src/builder/header.rs b/multiboot2-header/src/builder/header.rs index ff6ba7bb..2bf3ea93 100644 --- a/multiboot2-header/src/builder/header.rs +++ b/multiboot2-header/src/builder/header.rs @@ -255,6 +255,7 @@ impl HeaderBuilder { /// Adds information requests from the /// [`InformationRequestHeaderTagBuilder`] to the builder. #[must_use] + #[allow(clippy::missing_const_for_fn)] // only in Rust 1.70 necessary pub fn information_request_tag( mut self, information_request_tag: InformationRequestHeaderTagBuilder, diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 6357e71f..f43f9cfe 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -125,6 +125,7 @@ mod tests { /// This test is relevant to give library users flexebility in passing the /// struct around. #[test] + #[allow(clippy::missing_const_for_fn)] // only in Rust 1.70 necessary fn boot_information_is_send_and_sync() { fn accept(_: T) {} let bytes = AlignedBytes([ From 68d75ade453f5aab8e407a2d7eee6adfeac13466 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 22:13:39 +0200 Subject: [PATCH 34/35] multiboot2: Changelog --- multiboot2/Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index 2e3968c3..a8379a4e 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -9,8 +9,8 @@ This release contains a massive refactoring of various internals. Now, **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 provide external custom tags, you -should be fine. +leak to the public interface. If you don't use external custom tags, you +should be fine from any refactorings. Please note that **all previous releases** must be considered unsafe, as they contain UB. However, it is never clear how UB results in immediate incorrect From d5d90373f041fb1a159249e93f518cde826b76be Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 17 Aug 2024 22:15:00 +0200 Subject: [PATCH 35/35] chore: Release --- Cargo.lock | 15 ++++++++++++++- multiboot2/Cargo.toml | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a17cb6d5..806e722e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,19 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "multiboot2" version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7ef048d4783355163fd0c874aac3db54b919dc6a86dc29bb13f67308b114b0" +dependencies = [ + "bitflags", + "derive_more", + "log", + "ptr_meta", + "uefi-raw", +] + +[[package]] +name = "multiboot2" +version = "0.21.0" dependencies = [ "bitflags", "derive_more", @@ -41,7 +54,7 @@ name = "multiboot2-header" version = "0.4.0" dependencies = [ "derive_more", - "multiboot2", + "multiboot2 0.20.2", ] [[package]] diff --git a/multiboot2/Cargo.toml b/multiboot2/Cargo.toml index 1fd7de2b..989a91a7 100644 --- a/multiboot2/Cargo.toml +++ b/multiboot2/Cargo.toml @@ -6,7 +6,7 @@ Multiboot2-compliant bootloaders, such as GRUB. It supports all tags from the specification including full support for the sections of ELF files. This library is `no_std` and can be used in a Multiboot2-kernel. """ -version = "0.20.2" +version = "0.21.0" authors = [ "Philipp Oppermann ", "Calvin Lee ",