Skip to content

Commit a08365e

Browse files
authored
Merge pull request #227 from rust-osdev/mb-hdr
init multiboot2-common and use same safe memory abstractions also in multiboot2-header crate
2 parents b6dda27 + a10e2f8 commit a08365e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2753
-2466
lines changed

Cargo.lock

+14-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
resolver = "2"
33
members = [
44
"multiboot2",
5+
"multiboot2-common",
56
"multiboot2-header",
67
]
78
exclude = [
@@ -12,9 +13,11 @@ exclude = [
1213
bitflags = "2.6.0"
1314
derive_more = { version = "~0.99.18", default-features = false, features = ["display"] }
1415
log = { version = "~0.4", default-features = false }
16+
ptr_meta = { version = "~0.2", default-features = false }
1517

16-
# This way, the "multiboot2" dependency in the multiboot2-header crate can be
17-
# referenced by version, while still the repository version is used
18-
# transparently during local development.
18+
# This way, the corresponding crate dependency can be normalley referenced by
19+
# version, while still the repository version is used transparently during local
20+
# development.
1921
[patch.crates-io]
2022
multiboot2 = { path = "multiboot2" }
23+
multiboot2-common = { path = "multiboot2-common" }

integration-test/bins/Cargo.lock

+14-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration-test/bins/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ util = { path = "./util" }
2424
# transparently during local development.
2525
[patch.crates-io]
2626
multiboot2 = { path = "../../multiboot2" }
27+
multiboot2-common = { path = "../../multiboot2-common" }
28+
multiboot2-header = { path = "../../multiboot2-header" }

integration-test/bins/multiboot2_chainloader/src/loader.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use core::ops::Deref;
1+
use alloc::boxed::Box;
22
use elf_rs::{ElfFile, ProgramHeaderEntry, ProgramType};
33
use multiboot2::{
4-
BootLoaderNameTag, CommandLineTag, MemoryArea, MemoryAreaType, MemoryMapTag, ModuleTag,
5-
SmbiosTag,
4+
BootLoaderNameTag, CommandLineTag, MaybeDynSized, MemoryArea, MemoryAreaType, MemoryMapTag,
5+
ModuleTag, SmbiosTag,
66
};
77

88
/// Loads the first module into memory. Assumes that the module is a ELF file.
@@ -43,27 +43,27 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
4343
// that the basic data structures are usable.
4444

4545
// build MBI
46-
let mbi = multiboot2::builder::InformationBuilder::new()
47-
.bootloader_name_tag(&BootLoaderNameTag::new("mb2_integrationtest_chainloader"))
48-
.command_line_tag(&CommandLineTag::new("chainloaded YEAH"))
46+
let mbi = multiboot2::Builder::new()
47+
.bootloader(BootLoaderNameTag::new("mb2_integrationtest_chainloader"))
48+
.cmdline(CommandLineTag::new("chainloaded YEAH"))
4949
// random non-sense memory map
50-
.memory_map_tag(&MemoryMapTag::new(&[MemoryArea::new(
50+
.mmap(MemoryMapTag::new(&[MemoryArea::new(
5151
0,
5252
0xffffffff,
5353
MemoryAreaType::Reserved,
5454
)]))
55-
.add_module_tag(&ModuleTag::new(
55+
.add_module(ModuleTag::new(
5656
elf_mod.start as u32,
5757
elf_mod.end as u32,
5858
elf_mod.string.unwrap(),
5959
))
6060
// Test that we can add SmbiosTag multiple times.
61-
.add_tag(SmbiosTag::new(1, 1, &[1, 2, 3]).deref())
62-
.unwrap()
63-
.add_tag(SmbiosTag::new(1, 2, &[1, 2, 3]).deref())
64-
.expect("should allow tag multiple times")
61+
.add_smbios(SmbiosTag::new(1, 1, &[1, 2, 3]))
62+
.add_smbios(SmbiosTag::new(2, 3, &[4, 5, 6]))
6563
.build();
6664

65+
let mbi = Box::leak(mbi);
66+
6767
log::info!(
6868
"Handing over to ELF: {}",
6969
elf_mod.string.unwrap_or("<unknown>")
@@ -74,7 +74,7 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
7474
core::arch::asm!(
7575
"jmp *%ecx",
7676
in("eax") multiboot2::MAGIC,
77-
in("ebx") mbi.as_ptr() as u32,
77+
in("ebx") mbi.as_ptr(),
7878
in("ecx") elf.entry_point() as u32,
7979
options(noreturn, att_syntax));
8080
}

integration-test/bins/multiboot2_payload/src/verify/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use alloc::vec::Vec;
66
use multiboot2::BootInformation;
77

88
pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
9-
println!("{mbi:#x?}");
9+
println!("MBI: {mbi:#x?}");
1010
println!();
1111

1212
let bootloader = mbi

multiboot2-common/Cargo.toml

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[package]
2+
name = "multiboot2-common"
3+
description = """
4+
Common helpers for the `multiboot2` and `multiboot2-header` crates.
5+
"""
6+
version = "0.1.0"
7+
authors = [
8+
"Philipp Schuster <phip1611@gmail.com>"
9+
]
10+
license = "MIT/Apache-2.0"
11+
edition = "2021"
12+
categories = [
13+
"no-std",
14+
"no-std::no-alloc",
15+
]
16+
keywords = [
17+
"Multiboot2"
18+
]
19+
readme = "README.md"
20+
homepage = "https://github.com/rust-osdev/multiboot2"
21+
repository = "https://github.com/rust-osdev/multiboot2"
22+
documentation = "https://docs.rs/multiboot2-common"
23+
rust-version = "1.70"
24+
25+
[features]
26+
default = ["builder"]
27+
alloc = []
28+
builder = ["alloc"]
29+
unstable = []
30+
31+
32+
[dependencies]
33+
derive_more.workspace = true
34+
ptr_meta.workspace = true
35+
36+
[package.metadata.docs.rs]
37+
all-features = true

multiboot2-common/Changelog.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG for crate `multiboot2`
2+
3+
## 0.1.0 (2024-08-20)
4+
5+
Initial release.

multiboot2-common/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# multiboot2-common

multiboot2-common/src/boxed.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Module for [`new_boxed`].
2+
3+
use crate::{increase_to_alignment, Header, MaybeDynSized, ALIGNMENT};
4+
use alloc::boxed::Box;
5+
use core::alloc::Layout;
6+
use core::mem;
7+
use core::ops::Deref;
8+
use core::ptr;
9+
10+
/// Creates a new tag implementing [`MaybeDynSized`] on the heap. This works for
11+
/// sized and unsized tags. However, it only makes sense to use this for tags
12+
/// that are DSTs (unsized). For regular sized structs, you can just create a
13+
/// typical constructor and box the result.
14+
///
15+
/// The provided `header`' total size (see [`Header`]) will be set dynamically
16+
/// by this function using [`Header::set_size`]. However, it must contain all
17+
/// other relevant metadata or update it in the `set_size` callback.
18+
///
19+
/// # Parameters
20+
/// - `additional_bytes_slices`: Array of byte slices that should be included
21+
/// without additional padding in-between. You don't need to add the bytes
22+
/// for [`Header`], but only additional payload.
23+
#[must_use]
24+
pub fn new_boxed<T: MaybeDynSized<Metadata = usize> + ?Sized>(
25+
mut header: T::Header,
26+
additional_bytes_slices: &[&[u8]],
27+
) -> Box<T> {
28+
let additional_size = additional_bytes_slices
29+
.iter()
30+
.map(|b| b.len())
31+
.sum::<usize>();
32+
33+
let tag_size = mem::size_of::<T::Header>() + additional_size;
34+
header.set_size(tag_size);
35+
36+
// Allocation size is multiple of alignment.
37+
// See <https://doc.rust-lang.org/reference/type-layout.html>
38+
let alloc_size = increase_to_alignment(tag_size);
39+
let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
40+
let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
41+
assert!(!heap_ptr.is_null());
42+
43+
// write header
44+
{
45+
let len = mem::size_of::<T::Header>();
46+
let ptr = core::ptr::addr_of!(header);
47+
unsafe {
48+
ptr::copy_nonoverlapping(ptr.cast::<u8>(), heap_ptr, len);
49+
}
50+
}
51+
52+
// write body
53+
{
54+
let mut write_offset = mem::size_of::<T::Header>();
55+
for &bytes in additional_bytes_slices {
56+
let len = bytes.len();
57+
let src = bytes.as_ptr();
58+
unsafe {
59+
let dst = heap_ptr.add(write_offset);
60+
ptr::copy_nonoverlapping(src, dst, len);
61+
write_offset += len;
62+
}
63+
}
64+
}
65+
66+
// This is a fat pointer for DSTs and a thin pointer for sized `T`s.
67+
let ptr: *mut T = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(&header));
68+
let reference = unsafe { Box::from_raw(ptr) };
69+
70+
// If this panic triggers, there is a fundamental flaw in my logic. This is
71+
// not the fault of an API user.
72+
assert_eq!(
73+
mem::size_of_val(reference.deref()),
74+
alloc_size,
75+
"Allocation should match Rusts expectation"
76+
);
77+
78+
reference
79+
}
80+
81+
/// Clones a [`MaybeDynSized`] by calling [`new_boxed`].
82+
#[must_use]
83+
pub fn clone_dyn<T: MaybeDynSized<Metadata = usize> + ?Sized>(tag: &T) -> Box<T> {
84+
new_boxed(tag.header().clone(), &[tag.payload()])
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use super::*;
90+
use crate::test_utils::{DummyDstTag, DummyTestHeader};
91+
use crate::Tag;
92+
93+
#[test]
94+
fn test_new_boxed() {
95+
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
96+
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
97+
assert_eq!(tag.header().typ(), 42);
98+
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
99+
100+
// Test that bytes are added consecutively without gaps.
101+
let header = DummyTestHeader::new(0xdead_beef, 0);
102+
let tag = new_boxed::<DummyDstTag>(header, &[&[0], &[1], &[2, 3]]);
103+
assert_eq!(tag.header().typ(), 0xdead_beef);
104+
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
105+
}
106+
107+
#[test]
108+
fn test_clone_tag() {
109+
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
110+
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
111+
assert_eq!(tag.header().typ(), 42);
112+
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
113+
114+
let _cloned = clone_dyn(tag.as_ref());
115+
}
116+
}

0 commit comments

Comments
 (0)