Skip to content

Commit 4b11673

Browse files
committed
XXX multiboot2-hdr
1 parent 9b0c42b commit 4b11673

File tree

5 files changed

+108
-122
lines changed

5 files changed

+108
-122
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

multiboot2-common/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ pub mod iter;
3434
#[allow(unused)]
3535
pub mod test_utils;
3636

37-
pub use iter::TagBytesIter;
38-
3937
use core::fmt::Debug;
4038
use core::marker::PhantomData;
4139
use core::mem;

multiboot2-header/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ unstable = []
4141

4242
[dependencies]
4343
derive_more.workspace = true
44+
log.workspace = true
45+
ptr_meta.workspace = true
4446
multiboot2 = { version = "0.20.0", default-features = false }
47+
multiboot2-common = "0.1.0"
4548

4649
[package.metadata.docs.rs]
4750
all-features = true

multiboot2-header/src/header.rs

Lines changed: 101 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ use crate::{
66
};
77
use core::fmt::{Debug, Formatter};
88
use core::mem::size_of;
9+
use core::slice;
10+
use core::{mem, ptr};
11+
use multiboot2::{TagHeader, TagTrait};
12+
use multiboot2_common::iter::TagBytesIter;
13+
use multiboot2_common::{BytesRef, DynSizedStructure, Header, ALIGNMENT};
914

1015
/// Magic value for a [`Multiboot2Header`], as defined by the spec.
1116
pub const MAGIC: u32 = 0xe85250d6;
@@ -18,7 +23,7 @@ pub const MAGIC: u32 = 0xe85250d6;
1823
/// please look at `HeaderBuilder` (requires the `builder` feature).
1924
#[derive(Debug)]
2025
#[repr(transparent)]
21-
pub struct Multiboot2Header<'a>(&'a Multiboot2BasicHeader);
26+
pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
2227

2328
impl<'a> Multiboot2Header<'a> {
2429
/// Public constructor for this type with various validations.
@@ -35,21 +40,30 @@ impl<'a> Multiboot2Header<'a> {
3540
/// Multiboot2 header pointer.
3641
pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result<Self, LoadError> {
3742
// null or not aligned
38-
if ptr.is_null() || ptr.align_offset(8) != 0 {
43+
if ptr.is_null() || ptr.align_offset(ALIGNMENT) != 0 {
3944
return Err(LoadError::InvalidAddress);
4045
}
4146

42-
let reference = &*ptr;
47+
let header = &*ptr;
4348

44-
if reference.header_magic() != MAGIC {
49+
if header.header_magic() != MAGIC {
4550
return Err(LoadError::MagicNotFound);
4651
}
4752

48-
if !reference.verify_checksum() {
53+
if !header.verify_checksum() {
4954
return Err(LoadError::ChecksumMismatch);
5055
}
5156

52-
Ok(Self(reference))
57+
let slice = slice::from_raw_parts(ptr.cast::<u8>(), header.length as usize);
58+
let bytes = BytesRef::<Multiboot2BasicHeader>::try_from(slice).map_err(|e| {
59+
log::error!("Load error: {e:#?}");
60+
LoadError::InvalidAddress
61+
})?;
62+
let inner = DynSizedStructure::ref_from(bytes).map_err(|e| {
63+
log::error!("Load error: {e:#?}");
64+
LoadError::TooSmall
65+
})?;
66+
Ok(Self(inner))
5367
}
5468

5569
/// Find the header in a given slice.
@@ -60,7 +74,7 @@ impl<'a> Multiboot2Header<'a> {
6074
/// or because it is truncated), it returns a [`LoadError`].
6175
/// If there is no header, it returns `None`.
6276
pub fn find_header(buffer: &[u8]) -> Result<Option<(&[u8], u32)>, LoadError> {
63-
if buffer.as_ptr().align_offset(4) != 0 {
77+
if buffer.as_ptr().align_offset(ALIGNMENT) != 0 {
6478
return Err(LoadError::InvalidAddress);
6579
}
6680

@@ -102,35 +116,36 @@ impl<'a> Multiboot2Header<'a> {
102116
)))
103117
}
104118

119+
/// Returns a [`Multiboot2HeaderTagIter`].
120+
#[must_use]
121+
pub fn iter(&self) -> Multiboot2HeaderTagIter {
122+
Multiboot2HeaderTagIter::new(self.0.payload())
123+
}
124+
105125
/// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
106126
#[must_use]
107127
pub const fn verify_checksum(&self) -> bool {
108-
self.0.verify_checksum()
128+
self.0.header().verify_checksum()
109129
}
110130
/// Wrapper around [`Multiboot2BasicHeader::header_magic`].
111131
#[must_use]
112132
pub const fn header_magic(&self) -> u32 {
113-
self.0.header_magic()
133+
self.0.header().header_magic()
114134
}
115135
/// Wrapper around [`Multiboot2BasicHeader::arch`].
116136
#[must_use]
117137
pub const fn arch(&self) -> HeaderTagISA {
118-
self.0.arch()
138+
self.0.header().arch()
119139
}
120140
/// Wrapper around [`Multiboot2BasicHeader::length`].
121141
#[must_use]
122142
pub const fn length(&self) -> u32 {
123-
self.0.length()
143+
self.0.header().length()
124144
}
125145
/// Wrapper around [`Multiboot2BasicHeader::checksum`].
126146
#[must_use]
127147
pub const fn checksum(&self) -> u32 {
128-
self.0.checksum()
129-
}
130-
/// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
131-
#[must_use]
132-
pub fn iter(&self) -> Multiboot2HeaderTagIter {
133-
self.0.tag_iter()
148+
self.0.header().checksum()
134149
}
135150
/// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
136151
#[must_use]
@@ -204,7 +219,7 @@ impl<'a> Multiboot2Header<'a> {
204219

205220
fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTagHeader> {
206221
self.iter()
207-
.map(|tag| unsafe { tag.as_ref() }.unwrap())
222+
.map(|tag| )
208223
.find(|tag| tag.typ() == typ)
209224
}
210225
}
@@ -229,7 +244,7 @@ impl core::error::Error for LoadError {}
229244
/// The "basic" Multiboot2 header. This means only the properties, that are known during
230245
/// compile time. All other information are derived during runtime from the size property.
231246
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
232-
#[repr(C)]
247+
#[repr(C, align(8))]
233248
pub struct Multiboot2BasicHeader {
234249
/// Must be the value of [`MAGIC`].
235250
header_magic: u32,
@@ -290,28 +305,11 @@ impl Multiboot2BasicHeader {
290305
pub const fn checksum(&self) -> u32 {
291306
self.checksum
292307
}
308+
}
293309

294-
/// Returns a [`Multiboot2HeaderTagIter`].
295-
///
296-
/// # Panics
297-
/// See doc of [`Multiboot2HeaderTagIter`].
298-
#[must_use]
299-
pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
300-
let base_hdr_size = size_of::<Self>();
301-
if base_hdr_size == self.length as usize {
302-
panic!("No end tag!");
303-
}
304-
let tag_base_addr = self as *const Self;
305-
// cast to u8 so that the offset in bytes works correctly
306-
let tag_base_addr = tag_base_addr as *const u8;
307-
// tag_base_addr should now point behind the "static" members
308-
let tag_base_addr = unsafe { tag_base_addr.add(base_hdr_size) };
309-
// align pointer to 8 byte according to spec
310-
let tag_base_addr = unsafe { tag_base_addr.add(tag_base_addr.align_offset(8)) };
311-
// cast back
312-
let tag_base_addr = tag_base_addr as *const HeaderTagHeader;
313-
let tags_len = self.length as usize - base_hdr_size;
314-
Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32)
310+
impl Header for Multiboot2BasicHeader {
311+
fn payload_len(&self) -> usize {
312+
self.length as usize - size_of::<Self>()
315313
}
316314
}
317315

@@ -322,100 +320,85 @@ impl Debug for Multiboot2BasicHeader {
322320
.field("arch", &{ self.arch })
323321
.field("length", &{ self.length })
324322
.field("checksum", &{ self.checksum })
325-
.field("tags", &self.tag_iter())
323+
.field("tags", &self.iter())
326324
.finish()
327325
}
328326
}
329327

330-
/// Iterator over all tags of a Multiboot2 header. The number of items is derived
331-
/// by the size/length of the header.
332-
///
333-
/// # Panics
334-
/// Panics if the `length`-attribute doesn't match the number of found tags, there are
335-
/// more tags found than technically possible, or if there is more than one end tag.
336-
/// All of these errors come from bigger, underlying problems. Therefore, they are
337-
/// considered as "abort/panic" and not as recoverable errors.
338-
#[derive(Clone)]
339-
pub struct Multiboot2HeaderTagIter {
340-
/// 8-byte aligned base address
341-
base: *const HeaderTagHeader,
342-
/// Offset in bytes from the base address.
343-
/// Always <= than size.
344-
n: u32,
345-
/// Size / final value of [`Self::n`].
346-
size: u32,
347-
/// Counts the number of found tags. If more tags are found
348-
/// than technically possible, for example because the length property
349-
/// was invalid and there are hundreds of "End"-tags, we can use
350-
/// this and enforce a hard iteration limit.
351-
tag_count: u32,
352-
/// Marks if the end-tag was found. Together with `tag_count`, this
353-
/// further helps to improve safety when invalid length properties are given.
354-
end_tag_found: bool,
328+
/// A generic tag serving as base to cast to specific tags. This is a DST
329+
/// version of [`Multiboot2BasicHeader`] that solves various type and memory safety
330+
/// problems by having a type that owns the whole memory of a tag.
331+
#[derive(PartialEq, Eq, ptr_meta::Pointee)]
332+
#[repr(transparent)]
333+
pub struct GenericTag(DynSizedStructure<Multiboot2BasicHeader>);
334+
335+
impl GenericTag {
336+
/// Returns a reference to [`GenericTag`] to interpret the provided memory
337+
/// as [`GenericTag`].
338+
///
339+
/// # Panics
340+
// pub fn ref_from<'a>(bytes: &'a [u8]) -> Result<&'a Self, MemoryError> {
341+
// TODO return result
342+
pub fn ref_from<'a>(bytes: &'a [u8]) -> &'a Self {
343+
let bytes = BytesRef::<TagHeader>::try_from(bytes).unwrap();
344+
let inner = DynSizedStructure::ref_from(bytes).unwrap();
345+
unsafe { mem::transmute::<_, &'a Self>(inner) }
346+
}
347+
348+
/// Returns the underlying [`TagHeader`].
349+
pub const fn header(&self) -> &Multiboot2BasicHeader {
350+
self.0.header()
351+
}
352+
353+
#[cfg(all(test, feature = "builder"))]
354+
pub const fn payload(&self) -> &[u8] {
355+
self.0.payload()
356+
}
357+
358+
/// Casts the generic tag to a specific implementation.
359+
/// TODO use tag trait.
360+
pub fn cast<T: Sized>(&self) -> &T {
361+
let base_ptr = ptr::addr_of!(*self).cast::<T>();
362+
let tag = unsafe { &*base_ptr };
363+
tag
364+
}
355365
}
356366

357-
impl Multiboot2HeaderTagIter {
358-
fn new(base: *const HeaderTagHeader, size: u32) -> Self {
359-
// transform to byte pointer => offset works properly
360-
let base = base as *const u8;
361-
let base = unsafe { base.add(base.align_offset(8)) };
362-
let base = base as *const HeaderTagHeader;
363-
Self {
364-
base,
365-
n: 0,
366-
size,
367-
tag_count: 0,
368-
end_tag_found: false,
369-
}
367+
impl Debug for GenericTag {
368+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
369+
f.debug_struct("GenericTag")
370+
.field("header", self.0.header())
371+
.field("payload", &"<bytes>")
372+
.finish()
370373
}
371374
}
372375

373-
impl Iterator for Multiboot2HeaderTagIter {
374-
type Item = *const HeaderTagHeader;
376+
/// Iterates the tags of the header from the first tag to the end tag. The en
377+
/// tag is included.
378+
#[derive(Clone)]
379+
pub struct Multiboot2HeaderTagIter<'a>(TagBytesIter<'a, TagHeader>);
375380

376-
fn next(&mut self) -> Option<Self::Item> {
377-
// no more bytes left to check; length reached
378-
if self.n >= self.size {
379-
return None;
380-
}
381+
impl<'a> Multiboot2HeaderTagIter<'a> {
382+
/// Creates a new iterator.
383+
pub fn new(mem: &'a [u8]) -> Self {
384+
Self(TagBytesIter::new(mem))
385+
}
386+
}
381387

382-
// transform to byte ptr => offset works correctly
383-
let ptr = self.base as *const u8;
384-
let ptr = unsafe { ptr.add(self.n as usize) };
385-
let ptr = ptr as *const HeaderTagHeader;
386-
assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
387-
let tag = unsafe { &*ptr };
388-
assert!(
389-
tag.size() <= 500,
390-
"no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
391-
{ tag.size() }
392-
);
393-
assert!(
394-
tag.size() >= 8,
395-
"no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}",
396-
{ tag.size() }
397-
);
398-
assert!(
399-
!self.end_tag_found,
400-
"There is more than one end tag! Maybe the `length` property is invalid?"
401-
);
402-
self.n += tag.size();
403-
// 8-byte alignment of pointer address
404-
self.n += self.n % 8;
405-
self.tag_count += 1;
406-
if tag.typ() == HeaderTagType::End {
407-
self.end_tag_found = true;
408-
}
409-
assert!(self.tag_count < HeaderTagType::count(), "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?");
410-
Some(ptr)
388+
impl<'a> Iterator for Multiboot2HeaderTagIter<'a> {
389+
type Item = &'a GenericTag;
390+
391+
fn next(&mut self) -> Option<Self::Item> {
392+
let bytes = self.0.next()?;
393+
Some(GenericTag::ref_from(bytes.as_ref()))
411394
}
412395
}
413396

414-
impl Debug for Multiboot2HeaderTagIter {
397+
/*impl Debug for Multiboot2HeaderTagIter {
415398
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
416399
let mut debug = f.debug_list();
417400
self.clone().for_each(|t| unsafe {
418-
let typ = (*t).typ();
401+
let typ = (*t).header().typ();
419402
if typ == HeaderTagType::End {
420403
let entry = t as *const EndHeaderTag;
421404
let entry = &*(entry);
@@ -466,7 +449,7 @@ impl Debug for Multiboot2HeaderTagIter {
466449
});
467450
debug.finish()
468451
}
469-
}
452+
}*/
470453

471454
#[cfg(test)]
472455
mod tests {

multiboot2/src/tag.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use crate::{TagTrait, TagType, TagTypeId};
1313
use core::fmt::{Debug, Formatter};
1414
use core::mem;
1515
use core::ptr;
16-
use multiboot2_common::iter::TagBytesIter;
17-
use multiboot2_common::{BytesRef, DynSizedStructure, Header};
16+
use multiboot2_common::{iter::TagBytesIter, BytesRef, DynSizedStructure, Header};
1817

1918
/// The common header that all tags have in common. This type is ABI compatible.
2019
///

0 commit comments

Comments
 (0)