Skip to content

Commit 8d13a87

Browse files
committed
add SpecifiedOrCustomTagType[Serialized] abstraction
1 parent 81b849b commit 8d13a87

File tree

1 file changed

+293
-11
lines changed

1 file changed

+293
-11
lines changed

multiboot2/src/tag_type.rs

Lines changed: 293 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,49 @@
1-
//! Module for [`TagType`].
1+
//! Module for the basic Multiboot2 tag and corresponding tag types.
2+
//!
3+
//! In serialized form, each [`Tag`] has a `typ` field of type [`SpecifiedOrCustomTagTypeSerialized`].
4+
//! This is ABI-compatible with an `u32` value. From this type, you can construct
5+
//! [`SpecifiedOrCustomTagType`] that contains the variants [`SpecifiedOrCustomTagType::Specified`]
6+
//! and [`SpecifiedOrCustomTagType::Custom`]. This type is NOT ABI-compatible with u32. However,
7+
//! the payload of the two variants, either [`TagType`] or `u32`, is.
28
9+
use core::convert::TryFrom;
310
use core::fmt::{Debug, Formatter};
411
use core::hash::Hash;
512
use core::marker::PhantomData;
613

7-
/// Possible types of a Tag in the Multiboot2 Information Structure (MBI), therefore the value
14+
/// Serialized version of [`SpecifiedOrCustomTagType`]. Values `0`..[`TagType::MAX_INDEX`] belong to
15+
/// [`SpecifiedOrCustomTagType::Specified`]. All others are mapped to
16+
/// [`SpecifiedOrCustomTagType::Custom`].
17+
///
18+
/// A `SpecifiedOrCustomTagSerialized` can be constructed from [`u32`].
19+
#[derive(Copy, Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
20+
#[repr(transparent)]
21+
pub struct SpecifiedOrCustomTagTypeSerialized(u32);
22+
23+
/// Abstraction over Multiboot2 tag types that are either specified or custom.
24+
///
25+
/// The Multiboot2 spec doesn't disallow custom Multiboot2 tags.
26+
///
27+
/// A `SpecifiedOrCustomTagType` can be constructed from [`SpecifiedOrCustomTagSerialized`].
28+
#[derive(Copy, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)]
29+
pub enum SpecifiedOrCustomTagType {
30+
/// Specified tags, see [`TagType`].
31+
Specified(TagType),
32+
/// Custom tags. The value is bigger than the maximum value of [`TagType`].
33+
/// The inner value can never conflict with [`TagType`].
34+
Custom(u32),
35+
}
36+
37+
impl Debug for SpecifiedOrCustomTagType {
38+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
39+
match self {
40+
SpecifiedOrCustomTagType::Specified(tag_type) => <TagType as Debug>::fmt(tag_type, f),
41+
SpecifiedOrCustomTagType::Custom(custom) => write!(f, "Custom({custom:#x})"),
42+
}
43+
}
44+
}
45+
46+
/// Specified types of a Tag in the Multiboot2 Information Structure (MBI), therefore the value
847
/// of the the `typ` property. The names and values are taken from the example C code
948
/// at the bottom of the Multiboot2 specification.
1049
#[repr(u32)]
@@ -86,23 +125,226 @@ pub enum TagType {
86125
/// This tag contains image load base physical address. The spec tells
87126
/// "It is provided only if image has relocatable header tag." but experience showed
88127
/// that this is not true for at least GRUB 2.
89-
LoadBaseAddr = 21,
128+
LoadBaseAddr = TagType::MAX_INDEX,
129+
}
130+
131+
/// `From`-implementations for conversions between `u32` and tag type abstractions.
132+
///
133+
/// There is a bijection for all conversions between `u32`, [`SpecifiedOrCustomTagTypeSerialized`],
134+
/// and [`SpecifiedOrCustomTagType`].
135+
mod primitive_conversion_impls {
136+
use super::*;
137+
138+
impl From<u32> for SpecifiedOrCustomTagTypeSerialized {
139+
fn from(value: u32) -> Self {
140+
// SAFETY: the type has repr(transparent)
141+
unsafe { core::mem::transmute(value) }
142+
}
143+
}
144+
145+
impl From<SpecifiedOrCustomTagTypeSerialized> for u32 {
146+
fn from(value: SpecifiedOrCustomTagTypeSerialized) -> Self {
147+
value.0 as _
148+
}
149+
}
150+
151+
impl From<u32> for SpecifiedOrCustomTagType {
152+
fn from(value: u32) -> Self {
153+
let value = SpecifiedOrCustomTagTypeSerialized::from(value);
154+
SpecifiedOrCustomTagType::from(value)
155+
}
156+
}
157+
158+
impl From<SpecifiedOrCustomTagType> for u32 {
159+
fn from(value: SpecifiedOrCustomTagType) -> Self {
160+
match value {
161+
SpecifiedOrCustomTagType::Specified(tag_type) => u32::from(tag_type),
162+
SpecifiedOrCustomTagType::Custom(custom) => custom,
163+
}
164+
}
165+
}
166+
167+
impl From<u32> for TagType {
168+
fn from(value: u32) -> Self {
169+
// SAFETY: safe as TagType has repr(u32)
170+
unsafe { core::mem::transmute(value) }
171+
}
172+
}
173+
174+
impl From<TagType> for u32 {
175+
fn from(value: TagType) -> Self {
176+
value as _
177+
}
178+
}
90179
}
91180

92-
// each compare/equal direction must be implemented manually
93-
impl PartialEq<u32> for TagType {
94-
fn eq(&self, other: &u32) -> bool {
95-
*self as u32 == *other
181+
/// `From`-implementations for all sensible conversions between
182+
/// [`SpecifiedOrCustomTagTypeSerialized`], [`SpecifiedOrCustomTagType`], and [`TagType`].
183+
///
184+
/// - [`SpecifiedOrCustomTagType`] can be constructed from [`SpecifiedOrCustomTagTypeSerialized`]
185+
/// and [`TagType`]
186+
/// - [`SpecifiedOrCustomTagTypeSerialized`] can be constructed from [`SpecifiedOrCustomTagType`]
187+
/// and [`TagType`]
188+
/// - [`TagType`] can be constructed from [`SpecifiedOrCustomTagType`] via a `TryFrom`
189+
/// implementation.
190+
mod intermediate_conversion_impls {
191+
use super::*;
192+
193+
impl From<TagType> for SpecifiedOrCustomTagTypeSerialized {
194+
fn from(value: TagType) -> Self {
195+
let value = u32::from(value);
196+
value.into()
197+
}
198+
}
199+
200+
impl From<TagType> for SpecifiedOrCustomTagType {
201+
fn from(value: TagType) -> Self {
202+
Self::Specified(value)
203+
}
204+
}
205+
206+
impl From<SpecifiedOrCustomTagType> for SpecifiedOrCustomTagTypeSerialized {
207+
fn from(value: SpecifiedOrCustomTagType) -> Self {
208+
let value = match value {
209+
SpecifiedOrCustomTagType::Specified(tag) => u32::from(tag),
210+
SpecifiedOrCustomTagType::Custom(custom) => custom,
211+
};
212+
value.into()
213+
}
214+
}
215+
216+
impl TryFrom<SpecifiedOrCustomTagType> for TagType {
217+
// todo add meaningful error type
218+
type Error = ();
219+
220+
fn try_from(value: SpecifiedOrCustomTagType) -> Result<Self, Self::Error> {
221+
match value {
222+
SpecifiedOrCustomTagType::Specified(tag_type) => Ok(tag_type),
223+
SpecifiedOrCustomTagType::Custom(_) => Err(()),
224+
}
225+
}
226+
}
227+
228+
impl TryFrom<SpecifiedOrCustomTagTypeSerialized> for TagType {
229+
// todo add meaningful error type
230+
type Error = ();
231+
232+
fn try_from(value: SpecifiedOrCustomTagTypeSerialized) -> Result<Self, Self::Error> {
233+
let value = u32::from(value);
234+
let value = SpecifiedOrCustomTagType::from(value);
235+
TagType::try_from(value)
236+
}
237+
}
238+
239+
impl From<SpecifiedOrCustomTagTypeSerialized> for SpecifiedOrCustomTagType {
240+
fn from(value: SpecifiedOrCustomTagTypeSerialized) -> Self {
241+
match value.0 {
242+
value @ 0..=TagType::MAX_INDEX => Self::Specified(value.into()),
243+
custom => Self::Custom(custom),
244+
}
245+
}
96246
}
97247
}
98248

99-
// each compare/equal direction must be implemented manually
100-
impl PartialEq<TagType> for u32 {
101-
fn eq(&self, other: &TagType) -> bool {
102-
*self == *other as u32
249+
/// Implements `partial_eq` between all types ([`SpecifiedOrCustomTagTypeSerialized`],
250+
/// [`SpecifiedOrCustomTagType`], and [`TagType`]) for both directions. Two values equal if their
251+
/// `u32` representation is equal.
252+
mod partial_eq_impls {
253+
use super::*;
254+
255+
impl PartialEq<u32> for TagType {
256+
fn eq(&self, other: &u32) -> bool {
257+
let this = u32::from(*self);
258+
this == *other
259+
}
260+
}
261+
262+
// each compare/equal direction must be implemented manually
263+
impl PartialEq<TagType> for u32 {
264+
fn eq(&self, other: &TagType) -> bool {
265+
other.eq(self)
266+
}
267+
}
268+
269+
impl PartialEq<u32> for SpecifiedOrCustomTagType {
270+
fn eq(&self, other: &u32) -> bool {
271+
let this = u32::from(*self);
272+
this == *other
273+
}
274+
}
275+
276+
// each compare/equal direction must be implemented manually
277+
impl PartialEq<SpecifiedOrCustomTagType> for u32 {
278+
fn eq(&self, other: &SpecifiedOrCustomTagType) -> bool {
279+
other.eq(self)
280+
}
281+
}
282+
283+
impl PartialEq<u32> for SpecifiedOrCustomTagTypeSerialized {
284+
fn eq(&self, other: &u32) -> bool {
285+
let this = u32::from(*self);
286+
this == *other
287+
}
288+
}
289+
290+
// each compare/equal direction must be implemented manually
291+
impl PartialEq<SpecifiedOrCustomTagTypeSerialized> for u32 {
292+
fn eq(&self, other: &SpecifiedOrCustomTagTypeSerialized) -> bool {
293+
other.eq(self)
294+
}
295+
}
296+
297+
impl PartialEq<SpecifiedOrCustomTagTypeSerialized> for SpecifiedOrCustomTagType {
298+
fn eq(&self, other: &SpecifiedOrCustomTagTypeSerialized) -> bool {
299+
let this = u32::from(*self);
300+
let that = u32::from(*other);
301+
this == that
302+
}
303+
}
304+
305+
// each compare/equal direction must be implemented manually
306+
impl PartialEq<SpecifiedOrCustomTagType> for SpecifiedOrCustomTagTypeSerialized {
307+
fn eq(&self, other: &SpecifiedOrCustomTagType) -> bool {
308+
other.eq(self)
309+
}
310+
}
311+
312+
impl PartialEq<SpecifiedOrCustomTagTypeSerialized> for TagType {
313+
fn eq(&self, other: &SpecifiedOrCustomTagTypeSerialized) -> bool {
314+
let this = u32::from(*self);
315+
let that = u32::from(*other);
316+
this == that
317+
}
318+
}
319+
320+
// each compare/equal direction must be implemented manually
321+
impl PartialEq<TagType> for SpecifiedOrCustomTagTypeSerialized {
322+
fn eq(&self, other: &TagType) -> bool {
323+
other.eq(self)
324+
}
325+
}
326+
327+
impl PartialEq<SpecifiedOrCustomTagType> for TagType {
328+
fn eq(&self, other: &SpecifiedOrCustomTagType) -> bool {
329+
let this = u32::from(*self);
330+
let other = u32::from(*other);
331+
this == other
332+
}
333+
}
334+
335+
// each compare/equal direction must be implemented manually
336+
impl PartialEq<TagType> for SpecifiedOrCustomTagType {
337+
fn eq(&self, other: &TagType) -> bool {
338+
other.eq(self)
339+
}
103340
}
104341
}
105342

343+
impl TagType {
344+
/// The maximum index of specified Multiboot2 tag types.
345+
const MAX_INDEX: u32 = 21;
346+
}
347+
106348
/// All tags that could passed via the Multiboot2 information structure to a payload/program/kernel.
107349
/// Better not confuse this with the Multiboot2 header tags. They are something different.
108350
#[derive(Clone, Copy)]
@@ -170,6 +412,7 @@ impl<'a> Iterator for TagIter<'a> {
170412
#[cfg(test)]
171413
mod tests {
172414
use super::*;
415+
use std::mem::{align_of, size_of};
173416

174417
#[test]
175418
fn test_hashset() {
@@ -203,5 +446,44 @@ mod tests {
203446
fn test_partial_eq_u32() {
204447
assert_eq!(21, TagType::LoadBaseAddr);
205448
assert_eq!(TagType::LoadBaseAddr, 21);
449+
assert_eq!(21, SpecifiedOrCustomTagTypeSerialized(21));
450+
assert_eq!(SpecifiedOrCustomTagTypeSerialized(21), 21);
451+
assert_eq!(42, SpecifiedOrCustomTagType::Custom(42));
452+
assert_eq!(SpecifiedOrCustomTagType::Custom(42), 42);
453+
assert_eq!(
454+
TagType::LoadBaseAddr,
455+
SpecifiedOrCustomTagType::Specified(TagType::LoadBaseAddr)
456+
);
457+
assert_eq!(
458+
SpecifiedOrCustomTagType::Specified(TagType::LoadBaseAddr),
459+
TagType::LoadBaseAddr
460+
);
461+
}
462+
463+
/// Tests the construction of [`SpecifiedOrCustomTagType`] from primitive `u32` values.
464+
#[test]
465+
#[allow(non_snake_case)]
466+
fn test_SpecifiedOrCustomTag() {
467+
assert_eq!(
468+
size_of::<SpecifiedOrCustomTagTypeSerialized>(),
469+
size_of::<u32>()
470+
);
471+
assert_eq!(
472+
align_of::<SpecifiedOrCustomTagTypeSerialized>(),
473+
align_of::<u32>()
474+
);
475+
476+
let tag_cmdline: u32 = TagType::Cmdline.into();
477+
let tag_cmdline: SpecifiedOrCustomTagTypeSerialized = tag_cmdline.into();
478+
let tag_cmdline: SpecifiedOrCustomTagType = tag_cmdline.into();
479+
matches!(
480+
tag_cmdline,
481+
SpecifiedOrCustomTagType::Specified(TagType::Cmdline)
482+
);
483+
484+
let tag_custom: u32 = 0x1337;
485+
let tag_custom: SpecifiedOrCustomTagTypeSerialized = tag_custom.into();
486+
let tag_custom: SpecifiedOrCustomTagType = tag_custom.into();
487+
matches!(tag_custom, SpecifiedOrCustomTagType::Custom(0x1337));
206488
}
207489
}

0 commit comments

Comments
 (0)