Skip to content

Commit 0ba4ee6

Browse files
committed
multiboot2: consume multiboot2-common (3/N): GenericTag
1 parent fb7c805 commit 0ba4ee6

File tree

6 files changed

+83
-127
lines changed

6 files changed

+83
-127
lines changed

multiboot2-common/src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub enum DynSizedStructureError {
6262
SizeTooBig,
6363
}
6464

65+
// todo impl core::error::Error
66+
6567
/// A dynamically sized type with a common sized header and a dynamic amount of
6668
/// bytes that owns all its memory. It is fulfilling all memory requirements and
6769
/// guarantees of Multiboot2 structures and Rustc/Miri.
@@ -177,6 +179,31 @@ pub enum BytesRefError {
177179
MissingPadding,
178180
}
179181

182+
// todo impl core::error::Error
183+
184+
/// Wrapper type for all memory-related errors enums.
185+
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
186+
pub enum MemoryError {
187+
/// See [`BytesRefError`].
188+
BytesRef(BytesRefError),
189+
/// See [`DynSizedStructureError`].
190+
DynSizedStructure(DynSizedStructureError),
191+
}
192+
193+
impl From<BytesRefError> for MemoryError {
194+
fn from(value: BytesRefError) -> Self {
195+
Self::BytesRef(value)
196+
}
197+
}
198+
199+
impl From<DynSizedStructureError> for MemoryError {
200+
fn from(value: DynSizedStructureError) -> Self {
201+
Self::DynSizedStructure(value)
202+
}
203+
}
204+
205+
// todo impl core::error::Error
206+
180207
#[cfg(test)]
181208
mod tests {
182209
use super::*;

multiboot2/src/boot_loader_name.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl TagTrait for BootLoaderNameTag {
8787
#[cfg(test)]
8888
mod tests {
8989
use super::*;
90-
use crate::tag::{GenericTag, TagBytesRef};
90+
use crate::tag::GenericTag;
9191
use core::borrow::Borrow;
9292
use multiboot2_common::test_utils::AlignedBytes;
9393

@@ -106,8 +106,7 @@ mod tests {
106106
#[test]
107107
fn test_parse_str() {
108108
let bytes = get_bytes();
109-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
110-
let tag = GenericTag::ref_from(bytes);
109+
let tag = GenericTag::ref_from(bytes.borrow());
111110
let tag = tag.cast::<BootLoaderNameTag>();
112111
assert_eq!(tag.header.typ, TagType::BootLoaderName);
113112
assert_eq!(tag.name(), Ok("hello"));

multiboot2/src/command_line.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl TagTrait for CommandLineTag {
8181
#[cfg(test)]
8282
mod tests {
8383
use super::*;
84-
use crate::tag::{GenericTag, TagBytesRef};
84+
use crate::tag::GenericTag;
8585
use core::borrow::Borrow;
8686
use multiboot2_common::test_utils::AlignedBytes;
8787

@@ -100,8 +100,7 @@ mod tests {
100100
#[test]
101101
fn test_parse_str() {
102102
let bytes = get_bytes();
103-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
104-
let tag = GenericTag::ref_from(bytes);
103+
let tag = GenericTag::ref_from(bytes.borrow());
105104
let tag = tag.cast::<CommandLineTag>();
106105
assert_eq!(tag.header.typ, TagType::Cmdline);
107106
assert_eq!(tag.cmdline(), Ok("hello"));

multiboot2/src/module.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl<'a> Debug for ModuleIter<'a> {
128128
#[cfg(test)]
129129
mod tests {
130130
use super::*;
131-
use crate::tag::{GenericTag, TagBytesRef};
131+
use crate::tag::GenericTag;
132132
use core::borrow::Borrow;
133133
use multiboot2_common::test_utils::AlignedBytes;
134134

@@ -151,8 +151,7 @@ mod tests {
151151
#[test]
152152
fn test_parse_str() {
153153
let bytes = get_bytes();
154-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
155-
let tag = GenericTag::ref_from(bytes);
154+
let tag = GenericTag::ref_from(bytes.borrow());
156155
let tag = tag.cast::<ModuleTag>();
157156
assert_eq!(tag.header.typ, TagType::Module);
158157
assert_eq!(tag.cmdline(), Ok("hello"));

multiboot2/src/smbios.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl Debug for SmbiosTag {
7171
#[cfg(test)]
7272
mod tests {
7373
use super::*;
74-
use crate::tag::{GenericTag, TagBytesRef};
74+
use crate::tag::GenericTag;
7575
use core::borrow::Borrow;
7676
use multiboot2_common::test_utils::AlignedBytes;
7777

@@ -97,8 +97,7 @@ mod tests {
9797
#[test]
9898
fn test_parse() {
9999
let bytes = get_bytes();
100-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
101-
let tag = GenericTag::ref_from(bytes);
100+
let tag = GenericTag::ref_from(bytes.borrow());
102101
let tag = tag.cast::<SmbiosTag>();
103102
assert_eq!(tag.header.typ, TagType::Smbios);
104103
assert_eq!(tag.major, 7);

multiboot2/src/tag.rs

Lines changed: 48 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ use crate::util::increase_to_alignment;
1313
use crate::{TagTrait, TagType, TagTypeId};
1414
use core::fmt::{Debug, Formatter};
1515
use core::mem;
16-
use core::ops::Deref;
1716
use core::ptr;
18-
use multiboot2_common::ALIGNMENT;
17+
use multiboot2_common::{BytesRef, DynSizedStructure, Header};
1918

2019
/// The common header that all tags have in common. This type is ABI compatible.
2120
///
@@ -43,107 +42,48 @@ impl TagHeader {
4342
}
4443
}
4544

46-
/// Wraps a byte slice representing a tag, but guarantees that the memory
47-
/// requirements are fulfilled.
48-
///
49-
/// This is the only type that can be used to construct a [`GenericTag`].
50-
///
51-
/// The main reason for this dedicated type is to create fine-grained unit-tests
52-
/// for Miri.
53-
///
54-
/// # Memory Requirements (for Multiboot and Rust/Miri)
55-
/// - At least as big as a `size_of<TagHeader>()`
56-
/// - at least [`ALIGNMENT`]-aligned
57-
/// - Length is multiple of [`ALIGNMENT`]. In other words, there are enough
58-
/// padding bytes until so that pointer coming right after the last byte
59-
/// is [`ALIGNMENT`]-aligned
60-
#[derive(Clone, Debug, PartialEq, Eq)]
61-
#[repr(transparent)]
62-
pub struct TagBytesRef<'a>(&'a [u8]);
63-
64-
impl<'a> TryFrom<&'a [u8]> for TagBytesRef<'a> {
65-
type Error = MemoryError;
66-
67-
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
68-
if value.len() < mem::size_of::<TagHeader>() {
69-
return Err(MemoryError::MinLengthNotSatisfied);
70-
}
71-
// Doesn't work as expected: if align_of_val(&value[0]) < ALIGNMENT {
72-
if value.as_ptr().align_offset(ALIGNMENT) != 0 {
73-
return Err(MemoryError::WrongAlignment);
74-
}
75-
let padding_bytes = value.len() % ALIGNMENT;
76-
if padding_bytes != 0 {
77-
return Err(MemoryError::MissingPadding);
78-
}
79-
Ok(Self(value))
45+
impl Header for TagHeader {
46+
fn payload_len(&self) -> usize {
47+
self.size as usize - size_of::<Self>()
8048
}
8149
}
8250

83-
impl<'a> Deref for TagBytesRef<'a> {
84-
type Target = &'a [u8];
85-
86-
fn deref(&self) -> &Self::Target {
87-
&self.0
88-
}
89-
}
90-
91-
/// Errors that occur when constructing a [`TagBytesRef`].
92-
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
93-
pub enum MemoryError {
94-
/// The memory must be at least [`ALIGNMENT`]-aligned.
95-
WrongAlignment,
96-
/// The memory must cover at least the length of a [`TagHeader`].
97-
MinLengthNotSatisfied,
98-
/// The buffer misses the terminating padding to the next alignment
99-
/// boundary.
100-
// This is mainly relevant to satisfy Miri. As the spec also mandates an
101-
// alignment, we can rely on this property.
102-
MissingPadding,
103-
}
104-
10551
/// A generic tag serving as base to cast to specific tags. This is a DST
10652
/// version of [`TagHeader`] that solves various type and memory safety
10753
/// problems by having a type that owns the whole memory of a tag.
108-
#[derive(Eq, Ord, PartialEq, PartialOrd, ptr_meta::Pointee)]
109-
#[repr(C)]
110-
pub struct GenericTag {
111-
header: TagHeader,
112-
/// Payload of the tag that is reflected in the `size` attribute, thus, no
113-
/// padding bytes!
114-
payload: [u8],
115-
}
54+
#[derive(PartialEq, Eq, ptr_meta::Pointee)]
55+
#[repr(transparent)]
56+
pub struct GenericTag(DynSizedStructure<TagHeader>);
11657

11758
impl GenericTag {
118-
/// Base size of the DST struct without the dynamic part.
119-
const BASE_SIZE: usize = mem::size_of::<TagHeader>();
120-
121-
/// Creates a reference to a [`GenericTag`] from the provided `bytes`
122-
/// [`TagBytesRef`].
123-
pub(crate) fn ref_from(bytes: TagBytesRef) -> &Self {
124-
let header = bytes.as_ptr().cast::<TagHeader>();
125-
let header = unsafe { &*header };
126-
let dst_len = Self::dst_len(header);
127-
assert_eq!(header.size as usize, Self::BASE_SIZE + dst_len);
128-
129-
let generic_tag: *const Self = ptr_meta::from_raw_parts(bytes.as_ptr().cast(), dst_len);
130-
unsafe { &*generic_tag }
59+
/// Returns a reference to [`GenericTag`] to interpret the provided memory
60+
/// as [`GenericTag`].
61+
///
62+
/// # Panics
63+
// pub fn ref_from<'a>(bytes: &'a [u8]) -> Result<&'a Self, MemoryError> {
64+
// TODO return result
65+
pub fn ref_from<'a>(bytes: &'a [u8]) -> &'a Self {
66+
let bytes = BytesRef::<TagHeader>::try_from(bytes).unwrap();
67+
let inner = DynSizedStructure::ref_from(bytes).unwrap();
68+
let tag = unsafe { mem::transmute::<_, &'a Self>(inner) };
69+
tag
13170
}
13271

72+
/// Returns the underlying [`TagHeader`].
13373
pub const fn header(&self) -> &TagHeader {
134-
&self.header
74+
self.0.header()
13575
}
13676

13777
#[cfg(all(test, feature = "builder"))]
13878
pub const fn payload(&self) -> &[u8] {
139-
&self.payload
79+
self.0.payload()
14080
}
14181

14282
/// Casts the generic tag to a specific [`TagTrait`] implementation which
14383
/// may be a ZST or DST typed tag.
14484
pub fn cast<T: TagTrait + ?Sized>(&self) -> &T {
14585
let base_ptr = ptr::addr_of!(*self);
146-
let t_dst_size = T::dst_len(&self.header);
86+
let t_dst_size = T::dst_len(&self.0.header());
14787
let t_ptr = ptr_meta::from_raw_parts(base_ptr.cast(), t_dst_size);
14888
let t_ref = unsafe { &*t_ptr };
14989
assert_eq!(mem::size_of_val(self), mem::size_of_val(t_ref));
@@ -154,7 +94,7 @@ impl GenericTag {
15494
impl Debug for GenericTag {
15595
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
15696
f.debug_struct("GenericTag")
157-
.field("header", &self.header)
97+
.field("header", &self.0.header())
15898
.field("payload", &"<bytes>")
15999
.finish()
160100
}
@@ -164,8 +104,8 @@ impl TagTrait for GenericTag {
164104
const ID: TagType = TagType::Custom(0xffffffff);
165105

166106
fn dst_len(header: &TagHeader) -> usize {
167-
assert!(header.size as usize >= Self::BASE_SIZE);
168-
header.size as usize - Self::BASE_SIZE
107+
assert!(header.size as usize >= size_of::<TagHeader>());
108+
header.size as usize - size_of::<TagHeader>()
169109
}
170110
}
171111

@@ -227,16 +167,14 @@ impl<'a> Iterator for TagIter<'a> {
227167
&self.buffer[from..to]
228168
};
229169

230-
// Should never fail at this point.
231-
let tag_bytes = TagBytesRef::try_from(bytes).unwrap();
232-
233-
Some(GenericTag::ref_from(tag_bytes))
170+
Some(GenericTag::ref_from(bytes))
234171
}
235172
}
236173

237174
#[cfg(test)]
238175
mod tests {
239176
use super::*;
177+
use crate::TagType;
240178
use core::borrow::Borrow;
241179
use core::mem;
242180
use multiboot2_common::test_utils::AlignedBytes;
@@ -251,11 +189,10 @@ mod tests {
251189
0x13, 0x37, 0x13, 0x37,
252190
]);
253191

254-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
255-
let tag = GenericTag::ref_from(bytes);
256-
assert_eq!(tag.header.typ, 0xffff_ffff);
257-
assert_eq!(tag.header.size, 16);
258-
assert_eq!(tag.payload.len(), 8);
192+
let tag = GenericTag::ref_from(bytes.borrow());
193+
assert_eq!(tag.header().typ, 0xffff_ffff);
194+
assert_eq!(tag.header().size, 16);
195+
assert_eq!(tag.payload().len(), 8);
259196
}
260197

261198
#[test]
@@ -280,8 +217,7 @@ mod tests {
280217
0xef, 0xbe, 0xad, 0xde, /* field b: 0x1337_1337 */
281218
0x37, 0x13, 0x37, 0x13,
282219
]);
283-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
284-
let tag = GenericTag::ref_from(bytes);
220+
let tag = GenericTag::ref_from(bytes.borrow());
285221
let custom_tag = tag.cast::<CustomTag>();
286222

287223
assert_eq!(mem::size_of_val(custom_tag), 16);
@@ -316,8 +252,7 @@ mod tests {
316252
0x37, 0x13, 0x37, 0x13,
317253
]);
318254

319-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
320-
let tag = GenericTag::ref_from(bytes);
255+
let tag = GenericTag::ref_from(bytes.borrow());
321256
let custom_tag = tag.cast::<CustomDstTag>();
322257

323258
assert_eq!(mem::size_of_val(custom_tag), 16);
@@ -345,10 +280,9 @@ mod tests {
345280
0, 0, 0, 0, 0, 0
346281
],
347282
);
348-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
349-
let tag = GenericTag::ref_from(bytes);
350-
assert_eq!(tag.header.typ, TagType::Cmdline);
351-
assert_eq!(tag.header.size, 8 + 10);
283+
let tag = GenericTag::ref_from(bytes.borrow());
284+
assert_eq!(tag.header().typ, TagType::Cmdline);
285+
assert_eq!(tag.header().size, 8 + 10);
352286
}
353287

354288
#[test]
@@ -367,13 +301,12 @@ mod tests {
367301
0, 0, 0, 0, 0, 0
368302
],
369303
);
370-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
371-
let tag = GenericTag::ref_from(bytes);
304+
let tag = GenericTag::ref_from(bytes.borrow());
372305

373306
// Main objective here is also that this test passes Miri.
374307
let tag = tag.cast::<GenericTag>();
375-
assert_eq!(tag.header.typ, TagType::Cmdline);
376-
assert_eq!(tag.header.size, 8 + 10);
308+
assert_eq!(tag.header().typ, TagType::Cmdline);
309+
assert_eq!(tag.header().size, 8 + 10);
377310
}
378311

379312
#[test]
@@ -397,19 +330,19 @@ mod tests {
397330
);
398331
let mut iter = TagIter::new(bytes.borrow());
399332
let first = iter.next().unwrap();
400-
assert_eq!(first.header.typ, TagType::Custom(0xff));
401-
assert_eq!(first.header.size, 8);
402-
assert!(first.payload.is_empty());
333+
assert_eq!(first.header().typ, TagType::Custom(0xff));
334+
assert_eq!(first.header().size, 8);
335+
assert!(first.payload().is_empty());
403336

404337
let second = iter.next().unwrap();
405-
assert_eq!(second.header.typ, TagType::Custom(0xfe));
406-
assert_eq!(second.header.size, 12);
407-
assert_eq!(&second.payload, &[1, 2, 3, 4]);
338+
assert_eq!(second.header().typ, TagType::Custom(0xfe));
339+
assert_eq!(second.header().size, 12);
340+
assert_eq!(&second.payload(), &[1, 2, 3, 4]);
408341

409342
let third = iter.next().unwrap();
410-
assert_eq!(third.header.typ, TagType::End);
411-
assert_eq!(third.header.size, 8);
412-
assert!(first.payload.is_empty());
343+
assert_eq!(third.header().typ, TagType::End);
344+
assert_eq!(third.header().size, 8);
345+
assert!(first.payload().is_empty());
413346

414347
assert_eq!(iter.next(), None);
415348
}

0 commit comments

Comments
 (0)