|
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. |
2 | 8 |
|
| 9 | +use core::convert::TryFrom; |
3 | 10 | use core::fmt::{Debug, Formatter};
|
4 | 11 | use core::hash::Hash;
|
5 | 12 | use core::marker::PhantomData;
|
6 | 13 |
|
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 |
8 | 47 | /// of the the `typ` property. The names and values are taken from the example C code
|
9 | 48 | /// at the bottom of the Multiboot2 specification.
|
10 | 49 | #[repr(u32)]
|
@@ -86,23 +125,226 @@ pub enum TagType {
|
86 | 125 | /// This tag contains image load base physical address. The spec tells
|
87 | 126 | /// "It is provided only if image has relocatable header tag." but experience showed
|
88 | 127 | /// 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 | + } |
90 | 179 | }
|
91 | 180 |
|
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 | + } |
96 | 246 | }
|
97 | 247 | }
|
98 | 248 |
|
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 | + } |
103 | 340 | }
|
104 | 341 | }
|
105 | 342 |
|
| 343 | +impl TagType { |
| 344 | + /// The maximum index of specified Multiboot2 tag types. |
| 345 | + const MAX_INDEX: u32 = 21; |
| 346 | +} |
| 347 | + |
106 | 348 | /// All tags that could passed via the Multiboot2 information structure to a payload/program/kernel.
|
107 | 349 | /// Better not confuse this with the Multiboot2 header tags. They are something different.
|
108 | 350 | #[derive(Clone, Copy)]
|
@@ -170,6 +412,7 @@ impl<'a> Iterator for TagIter<'a> {
|
170 | 412 | #[cfg(test)]
|
171 | 413 | mod tests {
|
172 | 414 | use super::*;
|
| 415 | + use std::mem::{align_of, size_of}; |
173 | 416 |
|
174 | 417 | #[test]
|
175 | 418 | fn test_hashset() {
|
@@ -203,5 +446,44 @@ mod tests {
|
203 | 446 | fn test_partial_eq_u32() {
|
204 | 447 | assert_eq!(21, TagType::LoadBaseAddr);
|
205 | 448 | 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)); |
206 | 488 | }
|
207 | 489 | }
|
0 commit comments