Skip to content

Commit

Permalink
Merge pull request #227 from rust-osdev/mb-hdr
Browse files Browse the repository at this point in the history
init multiboot2-common and use same safe memory abstractions also in multiboot2-header crate
  • Loading branch information
phip1611 authored Aug 20, 2024
2 parents b6dda27 + a10e2f8 commit a08365e
Show file tree
Hide file tree
Showing 59 changed files with 2,753 additions and 2,466 deletions.
16 changes: 14 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"
members = [
"multiboot2",
"multiboot2-common",
"multiboot2-header",
]
exclude = [
Expand All @@ -12,9 +13,11 @@ exclude = [
bitflags = "2.6.0"
derive_more = { version = "~0.99.18", default-features = false, features = ["display"] }
log = { version = "~0.4", default-features = false }
ptr_meta = { version = "~0.2", default-features = false }

# 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.
# This way, the corresponding crate dependency can be normalley referenced by
# version, while still the repository version is used transparently during local
# development.
[patch.crates-io]
multiboot2 = { path = "multiboot2" }
multiboot2-common = { path = "multiboot2-common" }
16 changes: 14 additions & 2 deletions integration-test/bins/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions integration-test/bins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ util = { path = "./util" }
# transparently during local development.
[patch.crates-io]
multiboot2 = { path = "../../multiboot2" }
multiboot2-common = { path = "../../multiboot2-common" }
multiboot2-header = { path = "../../multiboot2-header" }
26 changes: 13 additions & 13 deletions integration-test/bins/multiboot2_chainloader/src/loader.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use core::ops::Deref;
use alloc::boxed::Box;
use elf_rs::{ElfFile, ProgramHeaderEntry, ProgramType};
use multiboot2::{
BootLoaderNameTag, CommandLineTag, MemoryArea, MemoryAreaType, MemoryMapTag, ModuleTag,
SmbiosTag,
BootLoaderNameTag, CommandLineTag, MaybeDynSized, MemoryArea, MemoryAreaType, MemoryMapTag,
ModuleTag, SmbiosTag,
};

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

// build MBI
let mbi = multiboot2::builder::InformationBuilder::new()
.bootloader_name_tag(&BootLoaderNameTag::new("mb2_integrationtest_chainloader"))
.command_line_tag(&CommandLineTag::new("chainloaded YEAH"))
let mbi = multiboot2::Builder::new()
.bootloader(BootLoaderNameTag::new("mb2_integrationtest_chainloader"))
.cmdline(CommandLineTag::new("chainloaded YEAH"))
// random non-sense memory map
.memory_map_tag(&MemoryMapTag::new(&[MemoryArea::new(
.mmap(MemoryMapTag::new(&[MemoryArea::new(
0,
0xffffffff,
MemoryAreaType::Reserved,
)]))
.add_module_tag(&ModuleTag::new(
.add_module(ModuleTag::new(
elf_mod.start as u32,
elf_mod.end as u32,
elf_mod.string.unwrap(),
))
// Test that we can add SmbiosTag multiple times.
.add_tag(SmbiosTag::new(1, 1, &[1, 2, 3]).deref())
.unwrap()
.add_tag(SmbiosTag::new(1, 2, &[1, 2, 3]).deref())
.expect("should allow tag multiple times")
.add_smbios(SmbiosTag::new(1, 1, &[1, 2, 3]))
.add_smbios(SmbiosTag::new(2, 3, &[4, 5, 6]))
.build();

let mbi = Box::leak(mbi);

log::info!(
"Handing over to ELF: {}",
elf_mod.string.unwrap_or("<unknown>")
Expand All @@ -74,7 +74,7 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
core::arch::asm!(
"jmp *%ecx",
in("eax") multiboot2::MAGIC,
in("ebx") mbi.as_ptr() as u32,
in("ebx") mbi.as_ptr(),
in("ecx") elf.entry_point() as u32,
options(noreturn, att_syntax));
}
Expand Down
2 changes: 1 addition & 1 deletion integration-test/bins/multiboot2_payload/src/verify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloc::vec::Vec;
use multiboot2::BootInformation;

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

let bootloader = mbi
Expand Down
37 changes: 37 additions & 0 deletions multiboot2-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "multiboot2-common"
description = """
Common helpers for the `multiboot2` and `multiboot2-header` crates.
"""
version = "0.1.0"
authors = [
"Philipp Schuster <[email protected]>"
]
license = "MIT/Apache-2.0"
edition = "2021"
categories = [
"no-std",
"no-std::no-alloc",
]
keywords = [
"Multiboot2"
]
readme = "README.md"
homepage = "https://github.com/rust-osdev/multiboot2"
repository = "https://github.com/rust-osdev/multiboot2"
documentation = "https://docs.rs/multiboot2-common"
rust-version = "1.70"

[features]
default = ["builder"]
alloc = []
builder = ["alloc"]
unstable = []


[dependencies]
derive_more.workspace = true
ptr_meta.workspace = true

[package.metadata.docs.rs]
all-features = true
5 changes: 5 additions & 0 deletions multiboot2-common/Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CHANGELOG for crate `multiboot2`

## 0.1.0 (2024-08-20)

Initial release.
1 change: 1 addition & 0 deletions multiboot2-common/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# multiboot2-common
116 changes: 116 additions & 0 deletions multiboot2-common/src/boxed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! Module for [`new_boxed`].
use crate::{increase_to_alignment, Header, MaybeDynSized, ALIGNMENT};
use alloc::boxed::Box;
use core::alloc::Layout;
use core::mem;
use core::ops::Deref;
use core::ptr;

/// Creates a new tag implementing [`MaybeDynSized`] on the heap. This works for
/// sized and unsized tags. However, it only makes sense to use this for tags
/// that are DSTs (unsized). For regular sized structs, you can just create a
/// typical constructor and box the result.
///
/// The provided `header`' total size (see [`Header`]) will be set dynamically
/// by this function using [`Header::set_size`]. However, it must contain all
/// other relevant metadata or update it in the `set_size` callback.
///
/// # Parameters
/// - `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 [`Header`], but only additional payload.
#[must_use]
pub fn new_boxed<T: MaybeDynSized<Metadata = usize> + ?Sized>(
mut header: T::Header,
additional_bytes_slices: &[&[u8]],
) -> Box<T> {
let additional_size = additional_bytes_slices
.iter()
.map(|b| b.len())
.sum::<usize>();

let tag_size = mem::size_of::<T::Header>() + additional_size;
header.set_size(tag_size);

// Allocation size is multiple of alignment.
// See <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, ALIGNMENT).unwrap();
let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
assert!(!heap_ptr.is_null());

// write header
{
let len = mem::size_of::<T::Header>();
let ptr = core::ptr::addr_of!(header);
unsafe {
ptr::copy_nonoverlapping(ptr.cast::<u8>(), heap_ptr, len);
}
}

// write body
{
let mut write_offset = mem::size_of::<T::Header>();
for &bytes in additional_bytes_slices {
let len = bytes.len();
let src = bytes.as_ptr();
unsafe {
let dst = heap_ptr.add(write_offset);
ptr::copy_nonoverlapping(src, dst, len);
write_offset += len;
}
}
}

// This is a fat pointer for DSTs and a thin pointer for sized `T`s.
let ptr: *mut T = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(&header));
let reference = unsafe { Box::from_raw(ptr) };

// If this panic triggers, there is a fundamental flaw in my logic. This is
// not the fault of an API user.
assert_eq!(
mem::size_of_val(reference.deref()),
alloc_size,
"Allocation should match Rusts expectation"
);

reference
}

/// Clones a [`MaybeDynSized`] by calling [`new_boxed`].
#[must_use]
pub fn clone_dyn<T: MaybeDynSized<Metadata = usize> + ?Sized>(tag: &T) -> Box<T> {
new_boxed(tag.header().clone(), &[tag.payload()])
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{DummyDstTag, DummyTestHeader};
use crate::Tag;

#[test]
fn test_new_boxed() {
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
assert_eq!(tag.header().typ(), 42);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);

// Test that bytes are added consecutively without gaps.
let header = DummyTestHeader::new(0xdead_beef, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0], &[1], &[2, 3]]);
assert_eq!(tag.header().typ(), 0xdead_beef);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
}

#[test]
fn test_clone_tag() {
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
assert_eq!(tag.header().typ(), 42);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);

let _cloned = clone_dyn(tag.as_ref());
}
}
Loading

0 comments on commit a08365e

Please sign in to comment.