multiboot2/
tag_type.rs

1//! Module for tag types.
2//!
3//! The relevant exports of this module are [`TagTypeId`] and [`TagType`].
4
5use core::fmt::{Debug, Formatter};
6use core::hash::Hash;
7
8/// Serialized form of [`TagType`] that matches the binary representation
9/// (`u32`).
10///
11/// The abstraction corresponds to the `typ`/`type` field of a Multiboot2
12/// [`TagHeader`]. This type can easily be created from or converted to
13/// [`TagType`].
14///
15/// [`TagHeader`]: crate::TagHeader
16#[repr(transparent)]
17#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
18pub struct TagTypeId(u32);
19
20impl TagTypeId {
21    /// Constructor.
22    #[must_use]
23    pub const fn new(val: u32) -> Self {
24        Self(val)
25    }
26}
27
28impl Debug for TagTypeId {
29    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
30        let tag_type = TagType::from(*self);
31        Debug::fmt(&tag_type, f)
32    }
33}
34
35/// Higher level abstraction for [`TagTypeId`] that assigns each possible value
36/// to a specific semantic according to the specification.
37///
38/// Additionally, it allows to use the [`TagType::Custom`] variant. It is
39/// **not binary compatible** with [`TagTypeId`].
40#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum TagType {
42    /// Tag `0`: Marks the end of the tags.
43    End,
44    /// Tag `1`: Additional command line string.
45    /// For example `''` or `'--my-custom-option foo --provided by_grub`, if
46    /// your GRUB config contains `multiboot2 /boot/multiboot2-binary.elf --my-custom-option foo --provided by_grub`
47    Cmdline,
48    /// Tag `2`: Name of the bootloader, e.g. 'GRUB 2.04-1ubuntu44.2'
49    BootLoaderName,
50    /// Tag `3`: Additional Multiboot modules, which are BLOBs provided in
51    /// memory. For example an initial ram disk with essential drivers.
52    Module,
53    /// Tag `4`: ‘mem_lower’ and ‘mem_upper’ indicate the amount of lower and
54    /// upper memory, respectively, in kilobytes. Lower memory starts at
55    /// address 0, and upper memory starts at address 1 megabyte. The maximum
56    /// possible value for lower memory is 640 kilobytes. The value returned
57    /// for upper memory is maximally the address of the first upper memory
58    /// hole minus 1 megabyte. It is not guaranteed to be this value.
59    ///
60    /// This tag may not be provided by some boot loaders on EFI platforms if
61    /// EFI boot services are enabled and available for the loaded image (EFI
62    /// boot services not terminated tag exists in Multiboot2 information
63    /// structure).
64    BasicMeminfo,
65    /// Tag `5`: This tag indicates which BIOS disk device the boot loader
66    /// loaded the OS image from. If the OS image was not loaded from a BIOS
67    /// disk, then this tag must not be present. The operating system may use
68    /// this field as a hint for determining its own root device, but is not
69    /// required to.
70    Bootdev,
71    /// Tag `6`: Memory map. The map provided is guaranteed to list all
72    /// standard RAM that should be available for normal use. This type however
73    /// includes the regions occupied by kernel, mbi, segments and modules.
74    /// Kernel must take care not to overwrite these regions.
75    ///
76    /// This tag may not be provided by some boot loaders on EFI platforms if
77    /// EFI boot services are enabled and available for the loaded image (EFI
78    /// boot services not terminated tag exists in Multiboot2 information
79    /// structure).
80    Mmap,
81    /// Tag `7`: Contains the VBE control information returned by the VBE
82    /// Function `0x00` and VBE mode information returned by the VBE Function
83    /// `0x01`, respectively. Note that VBE 3.0 defines another protected mode
84    /// interface which is incompatible with the old one. If you want to use
85    /// the new protected mode interface, you will have to find the table
86    /// yourself.
87    Vbe,
88    /// Tag `8`: Framebuffer.
89    Framebuffer,
90    /// Tag `9`: This tag contains section header table from an ELF kernel, the
91    /// size of each entry, number of entries, and the string table used as the
92    /// index of names. They correspond to the `shdr_*` entries (`shdr_num`,
93    /// etc.) in the Executable and Linkable Format (ELF) specification in the
94    /// program header.
95    ElfSections,
96    /// Tag `10`: APM table. See Advanced Power Management (APM) BIOS Interface
97    /// Specification, for more information.
98    Apm,
99    /// Tag `11`: This tag contains pointer to i386 EFI system table.
100    Efi32,
101    /// Tag `21`: This tag contains pointer to amd64 EFI system table.
102    Efi64,
103    /// Tag `13`: This tag contains a copy of SMBIOS tables as well as their
104    /// version.
105    Smbios,
106    /// Tag `14`: Also called "AcpiOld" in other multiboot2 implementations.
107    AcpiV1,
108    /// Tag `15`: Refers to version 2 and later of Acpi.
109    /// Also called "AcpiNew" in other multiboot2 implementations.
110    AcpiV2,
111    /// Tag `16`: This tag contains network information in the format specified
112    /// as DHCP. It may be either a real DHCP reply or just the configuration
113    /// info in the same format. This tag appears once
114    /// per card.
115    Network,
116    /// Tag `17`: This tag contains EFI memory map as per EFI specification.
117    /// This tag may not be provided by some boot loaders on EFI platforms if
118    /// EFI boot services are enabled and available for the loaded image (EFI
119    /// boot services not terminated tag exists in Multiboot2 information
120    /// structure).
121    EfiMmap,
122    /// Tag `18`: This tag indicates ExitBootServices wasn't called.
123    EfiBs,
124    /// Tag `19`: This tag contains pointer to EFI i386 image handle. Usually
125    /// it is boot loader image handle.
126    Efi32Ih,
127    /// Tag `20`: This tag contains pointer to EFI amd64 image handle. Usually
128    /// it is boot loader image handle.
129    Efi64Ih,
130    /// Tag `21`: This tag contains image load base physical address. The spec
131    /// tells *"It is provided only if image has relocatable header tag."* but
132    /// experience showed that this is not true for at least GRUB 2.
133    LoadBaseAddr,
134    /// Custom tag types `> 21`. The Multiboot2 spec doesn't explicitly allow
135    /// or disallow them. Bootloader and OS developers are free to use custom
136    /// tags.
137    Custom(u32),
138}
139
140impl TagType {
141    /// Convenient wrapper to get the underlying `u32` representation of the tag.
142    #[must_use]
143    pub fn val(&self) -> u32 {
144        u32::from(*self)
145    }
146}
147
148/// Relevant `From`-implementations for conversions between `u32`, [´TagTypeId´]
149/// and [´TagType´].
150mod primitive_conversion_impls {
151    use super::*;
152
153    impl From<u32> for TagTypeId {
154        fn from(value: u32) -> Self {
155            // SAFETY: the type has repr(transparent)
156            unsafe { core::mem::transmute(value) }
157        }
158    }
159
160    impl From<TagTypeId> for u32 {
161        fn from(value: TagTypeId) -> Self {
162            value.0 as _
163        }
164    }
165
166    impl From<u32> for TagType {
167        fn from(value: u32) -> Self {
168            match value {
169                0 => Self::End,
170                1 => Self::Cmdline,
171                2 => Self::BootLoaderName,
172                3 => Self::Module,
173                4 => Self::BasicMeminfo,
174                5 => Self::Bootdev,
175                6 => Self::Mmap,
176                7 => Self::Vbe,
177                8 => Self::Framebuffer,
178                9 => Self::ElfSections,
179                10 => Self::Apm,
180                11 => Self::Efi32,
181                12 => Self::Efi64,
182                13 => Self::Smbios,
183                14 => Self::AcpiV1,
184                15 => Self::AcpiV2,
185                16 => Self::Network,
186                17 => Self::EfiMmap,
187                18 => Self::EfiBs,
188                19 => Self::Efi32Ih,
189                20 => Self::Efi64Ih,
190                21 => Self::LoadBaseAddr,
191                c => Self::Custom(c),
192            }
193        }
194    }
195
196    impl From<TagType> for u32 {
197        fn from(value: TagType) -> Self {
198            match value {
199                TagType::End => 0,
200                TagType::Cmdline => 1,
201                TagType::BootLoaderName => 2,
202                TagType::Module => 3,
203                TagType::BasicMeminfo => 4,
204                TagType::Bootdev => 5,
205                TagType::Mmap => 6,
206                TagType::Vbe => 7,
207                TagType::Framebuffer => 8,
208                TagType::ElfSections => 9,
209                TagType::Apm => 10,
210                TagType::Efi32 => 11,
211                TagType::Efi64 => 12,
212                TagType::Smbios => 13,
213                TagType::AcpiV1 => 14,
214                TagType::AcpiV2 => 15,
215                TagType::Network => 16,
216                TagType::EfiMmap => 17,
217                TagType::EfiBs => 18,
218                TagType::Efi32Ih => 19,
219                TagType::Efi64Ih => 20,
220                TagType::LoadBaseAddr => 21,
221                TagType::Custom(c) => c,
222            }
223        }
224    }
225}
226
227/// `From`-implementations for conversions between [´TagTypeId´] and [´TagType´].
228mod intermediate_conversion_impls {
229    use super::*;
230
231    impl From<TagTypeId> for TagType {
232        fn from(value: TagTypeId) -> Self {
233            let value = u32::from(value);
234            Self::from(value)
235        }
236    }
237
238    impl From<TagType> for TagTypeId {
239        fn from(value: TagType) -> Self {
240            let value = u32::from(value);
241            Self::from(value)
242        }
243    }
244}
245
246/// Implements `partial_eq` between [´TagTypeId´] and [´TagType´]. Two values
247/// are equal if their `u32` representation is equal. Additionally, `u32` can
248/// be compared with [´TagTypeId´].
249mod partial_eq_impls {
250    use super::*;
251
252    impl PartialEq<TagTypeId> for TagType {
253        fn eq(&self, other: &TagTypeId) -> bool {
254            let this = u32::from(*self);
255            let that = u32::from(*other);
256            this == that
257        }
258    }
259
260    // each compare/equal direction must be implemented manually
261    impl PartialEq<TagType> for TagTypeId {
262        fn eq(&self, other: &TagType) -> bool {
263            other.eq(self)
264        }
265    }
266
267    impl PartialEq<u32> for TagTypeId {
268        fn eq(&self, other: &u32) -> bool {
269            let this = u32::from(*self);
270            this == *other
271        }
272    }
273
274    impl PartialEq<TagTypeId> for u32 {
275        fn eq(&self, other: &TagTypeId) -> bool {
276            other.eq(self)
277        }
278    }
279
280    impl PartialEq<u32> for TagType {
281        fn eq(&self, other: &u32) -> bool {
282            let this = u32::from(*self);
283            this == *other
284        }
285    }
286
287    impl PartialEq<TagType> for u32 {
288        fn eq(&self, other: &TagType) -> bool {
289            other.eq(self)
290        }
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297    use std::mem::{align_of, size_of};
298
299    #[test]
300    fn test_hashset() {
301        let mut set = std::collections::HashSet::new();
302        set.insert(TagType::Cmdline);
303        set.insert(TagType::ElfSections);
304        set.insert(TagType::BootLoaderName);
305        set.insert(TagType::LoadBaseAddr);
306        set.insert(TagType::LoadBaseAddr);
307        assert_eq!(set.len(), 4);
308        println!("{set:#?}");
309    }
310
311    #[test]
312    fn test_btreeset() {
313        let mut set = std::collections::BTreeSet::new();
314        set.insert(TagType::Cmdline);
315        set.insert(TagType::ElfSections);
316        set.insert(TagType::BootLoaderName);
317        set.insert(TagType::LoadBaseAddr);
318        set.insert(TagType::LoadBaseAddr);
319        assert_eq!(set.len(), 4);
320        for (current, next) in set.iter().zip(set.iter().skip(1)) {
321            assert!(current < next);
322        }
323        println!("{set:#?}");
324    }
325
326    /// Tests for equality when one type is u32 and the other the enum representation.
327    #[test]
328    fn test_partial_eq_u32() {
329        assert_eq!(21, TagType::LoadBaseAddr);
330        assert_eq!(TagType::LoadBaseAddr, 21);
331        assert_eq!(21, TagTypeId(21));
332        assert_eq!(TagTypeId(21), 21);
333        assert_eq!(42, TagType::Custom(42));
334        assert_eq!(TagType::Custom(42), 42);
335    }
336
337    /// Tests the construction of [`TagTypeId`] from primitive `u32` values.
338    #[test]
339    #[allow(non_snake_case)]
340    fn test_TagTypeId() {
341        assert_eq!(size_of::<TagTypeId>(), size_of::<u32>());
342        assert_eq!(align_of::<TagTypeId>(), align_of::<u32>());
343
344        for i in 0..50_u32 {
345            let val: TagTypeId = i.into();
346            let val2: TagType = val.into();
347            assert_eq!(val, val2);
348        }
349
350        let tag_custom: u32 = 0x1337;
351        let tag_custom: TagTypeId = tag_custom.into();
352        let tag_custom: TagType = tag_custom.into();
353        matches!(tag_custom, TagType::Custom(0x1337));
354    }
355
356    /// Tests the construction of [`TagTypeId`] from primitive `u32` values for
357    /// specified and custom tags.
358    #[test]
359    #[allow(non_snake_case)]
360    fn test_from_and_to_tag_type_id() {
361        for i in 0..1_000 {
362            let tag_type_id = TagTypeId::new(i);
363            let tag_type_from_id = TagType::from(tag_type_id);
364            let tag_type_from_u16 = TagType::from(i);
365            assert_eq!(tag_type_from_id, tag_type_from_u16)
366        }
367    }
368}