1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
//! Module for tag types.
//!
//! The relevant exports of this module are [`TagTypeId`] and [`TagType`].

use core::fmt::{Debug, Formatter};
use core::hash::Hash;

/// Serialized form of [`TagType`] that matches the binary representation
/// (`u32`). The abstraction corresponds to the `typ`/`type` field of a
/// Multiboot2 [`Tag`]. This type can easily be created from or converted to
/// [`TagType`].
///
/// [`Tag`]: crate::Tag
#[repr(transparent)]
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
pub struct TagTypeId(u32);

impl TagTypeId {
    /// Constructor.
    pub fn new(val: u32) -> Self {
        Self(val)
    }
}

impl Debug for TagTypeId {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        let tag_type = TagType::from(*self);
        Debug::fmt(&tag_type, f)
    }
}

/// Higher level abstraction for [`TagTypeId`] that assigns each possible value
/// to a specific semantic according to the specification. Additionally, it
/// allows to use the [`TagType::Custom`] variant. It is **not binary compatible**
/// with [`TagTypeId`].
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TagType {
    /// Tag `0`: Marks the end of the tags.
    End,
    /// Tag `1`: Additional command line string.
    /// For example `''` or `'--my-custom-option foo --provided by_grub`, if
    /// your GRUB config contains `multiboot2 /boot/multiboot2-binary.elf --my-custom-option foo --provided by_grub`
    Cmdline,
    /// Tag `2`: Name of the bootloader, e.g. 'GRUB 2.04-1ubuntu44.2'
    BootLoaderName,
    /// Tag `3`: Additional Multiboot modules, which are BLOBs provided in
    /// memory. For example an initial ram disk with essential drivers.
    Module,
    /// Tag `4`: ‘mem_lower’ and ‘mem_upper’ indicate the amount of lower and
    /// upper memory, respectively, in kilobytes. Lower memory starts at
    /// address 0, and upper memory starts at address 1 megabyte. The maximum
    /// possible value for lower memory is 640 kilobytes. The value returned
    /// for upper memory is maximally the address of the first upper memory
    /// hole minus 1 megabyte. It is not guaranteed to be this value.
    ///
    /// This tag may not be provided by some boot loaders on EFI platforms if
    /// EFI boot services are enabled and available for the loaded image (EFI
    /// boot services not terminated tag exists in Multiboot2 information
    /// structure).
    BasicMeminfo,
    /// Tag `5`: This tag indicates which BIOS disk device the boot loader
    /// loaded the OS image from. If the OS image was not loaded from a BIOS
    /// disk, then this tag must not be present. The operating system may use
    /// this field as a hint for determining its own root device, but is not
    /// required to.
    Bootdev,
    /// Tag `6`: Memory map. The map provided is guaranteed to list all
    /// standard RAM that should be available for normal use. This type however
    /// includes the regions occupied by kernel, mbi, segments and modules.
    /// Kernel must take care not to overwrite these regions.
    ///
    /// This tag may not be provided by some boot loaders on EFI platforms if
    /// EFI boot services are enabled and available for the loaded image (EFI
    /// boot services not terminated tag exists in Multiboot2 information
    /// structure).
    Mmap,
    /// Tag `7`: Contains the VBE control information returned by the VBE
    /// Function `0x00` and VBE mode information returned by the VBE Function
    /// `0x01`, respectively. Note that VBE 3.0 defines another protected mode
    /// interface which is incompatible with the old one. If you want to use
    /// the new protected mode interface, you will have to find the table
    /// yourself.
    Vbe,
    /// Tag `8`: Framebuffer.
    Framebuffer,
    /// Tag `9`: This tag contains section header table from an ELF kernel, the
    /// size of each entry, number of entries, and the string table used as the
    /// index of names. They correspond to the `shdr_*` entries (`shdr_num`,
    /// etc.) in the Executable and Linkable Format (ELF) specification in the
    /// program header.
    ElfSections,
    /// Tag `10`: APM table. See Advanced Power Management (APM) BIOS Interface
    /// Specification, for more information.
    Apm,
    /// Tag `11`: This tag contains pointer to i386 EFI system table.
    Efi32,
    /// Tag `21`: This tag contains pointer to amd64 EFI system table.
    Efi64,
    /// Tag `13`: This tag contains a copy of SMBIOS tables as well as their
    /// version.
    Smbios,
    /// Tag `14`: Also called "AcpiOld" in other multiboot2 implementations.
    AcpiV1,
    /// Tag `15`: Refers to version 2 and later of Acpi.
    /// Also called "AcpiNew" in other multiboot2 implementations.
    AcpiV2,
    /// Tag `16`: This tag contains network information in the format specified
    /// as DHCP. It may be either a real DHCP reply or just the configuration
    /// info in the same format. This tag appears once
    /// per card.
    Network,
    /// Tag `17`: This tag contains EFI memory map as per EFI specification.
    /// This tag may not be provided by some boot loaders on EFI platforms if
    /// EFI boot services are enabled and available for the loaded image (EFI
    /// boot services not terminated tag exists in Multiboot2 information
    /// structure).
    EfiMmap,
    /// Tag `18`: This tag indicates ExitBootServices wasn't called.
    EfiBs,
    /// Tag `19`: This tag contains pointer to EFI i386 image handle. Usually
    /// it is boot loader image handle.
    Efi32Ih,
    /// Tag `20`: This tag contains pointer to EFI amd64 image handle. Usually
    /// it is boot loader image handle.
    Efi64Ih,
    /// Tag `21`: This tag contains image load base physical address. The spec
    /// tells *"It is provided only if image has relocatable header tag."* but
    /// experience showed that this is not true for at least GRUB 2.
    LoadBaseAddr,
    /// Custom tag types `> 21`. The Multiboot2 spec doesn't explicitly allow
    /// or disallow them. Bootloader and OS developers are free to use custom
    /// tags.
    Custom(u32),
}

impl TagType {
    /// Convenient wrapper to get the underlying `u32` representation of the tag.
    pub fn val(&self) -> u32 {
        u32::from(*self)
    }
}

/// Relevant `From`-implementations for conversions between `u32`, [´TagTypeId´]
/// and [´TagType´].
mod primitive_conversion_impls {
    use super::*;

    impl From<u32> for TagTypeId {
        fn from(value: u32) -> Self {
            // SAFETY: the type has repr(transparent)
            unsafe { core::mem::transmute(value) }
        }
    }

    impl From<TagTypeId> for u32 {
        fn from(value: TagTypeId) -> Self {
            value.0 as _
        }
    }

    impl From<u32> for TagType {
        fn from(value: u32) -> Self {
            match value {
                0 => TagType::End,
                1 => TagType::Cmdline,
                2 => TagType::BootLoaderName,
                3 => TagType::Module,
                4 => TagType::BasicMeminfo,
                5 => TagType::Bootdev,
                6 => TagType::Mmap,
                7 => TagType::Vbe,
                8 => TagType::Framebuffer,
                9 => TagType::ElfSections,
                10 => TagType::Apm,
                11 => TagType::Efi32,
                12 => TagType::Efi64,
                13 => TagType::Smbios,
                14 => TagType::AcpiV1,
                15 => TagType::AcpiV2,
                16 => TagType::Network,
                17 => TagType::EfiMmap,
                18 => TagType::EfiBs,
                19 => TagType::Efi32Ih,
                20 => TagType::Efi64Ih,
                21 => TagType::LoadBaseAddr,
                c => TagType::Custom(c),
            }
        }
    }

    impl From<TagType> for u32 {
        fn from(value: TagType) -> Self {
            match value {
                TagType::End => 0,
                TagType::Cmdline => 1,
                TagType::BootLoaderName => 2,
                TagType::Module => 3,
                TagType::BasicMeminfo => 4,
                TagType::Bootdev => 5,
                TagType::Mmap => 6,
                TagType::Vbe => 7,
                TagType::Framebuffer => 8,
                TagType::ElfSections => 9,
                TagType::Apm => 10,
                TagType::Efi32 => 11,
                TagType::Efi64 => 12,
                TagType::Smbios => 13,
                TagType::AcpiV1 => 14,
                TagType::AcpiV2 => 15,
                TagType::Network => 16,
                TagType::EfiMmap => 17,
                TagType::EfiBs => 18,
                TagType::Efi32Ih => 19,
                TagType::Efi64Ih => 20,
                TagType::LoadBaseAddr => 21,
                TagType::Custom(c) => c,
            }
        }
    }
}

/// `From`-implementations for conversions between [´TagTypeId´] and [´TagType´].
mod intermediate_conversion_impls {
    use super::*;

    impl From<TagTypeId> for TagType {
        fn from(value: TagTypeId) -> Self {
            let value = u32::from(value);
            TagType::from(value)
        }
    }

    impl From<TagType> for TagTypeId {
        fn from(value: TagType) -> Self {
            let value = u32::from(value);
            TagTypeId::from(value)
        }
    }
}

/// Implements `partial_eq` between [´TagTypeId´] and [´TagType´]. Two values
/// are equal if their `u32` representation is equal. Additionally, `u32` can
/// be compared with [´TagTypeId´].
mod partial_eq_impls {
    use super::*;

    impl PartialEq<TagTypeId> for TagType {
        fn eq(&self, other: &TagTypeId) -> bool {
            let this = u32::from(*self);
            let that = u32::from(*other);
            this == that
        }
    }

    // each compare/equal direction must be implemented manually
    impl PartialEq<TagType> for TagTypeId {
        fn eq(&self, other: &TagType) -> bool {
            other.eq(self)
        }
    }

    impl PartialEq<u32> for TagTypeId {
        fn eq(&self, other: &u32) -> bool {
            let this = u32::from(*self);
            this == *other
        }
    }

    impl PartialEq<TagTypeId> for u32 {
        fn eq(&self, other: &TagTypeId) -> bool {
            other.eq(self)
        }
    }

    impl PartialEq<u32> for TagType {
        fn eq(&self, other: &u32) -> bool {
            let this = u32::from(*self);
            this == *other
        }
    }

    impl PartialEq<TagType> for u32 {
        fn eq(&self, other: &TagType) -> bool {
            other.eq(self)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem::{align_of, size_of};

    #[test]
    fn test_hashset() {
        let mut set = std::collections::HashSet::new();
        set.insert(TagType::Cmdline);
        set.insert(TagType::ElfSections);
        set.insert(TagType::BootLoaderName);
        set.insert(TagType::LoadBaseAddr);
        set.insert(TagType::LoadBaseAddr);
        assert_eq!(set.len(), 4);
        println!("{:#?}", set);
    }

    #[test]
    fn test_btreeset() {
        let mut set = std::collections::BTreeSet::new();
        set.insert(TagType::Cmdline);
        set.insert(TagType::ElfSections);
        set.insert(TagType::BootLoaderName);
        set.insert(TagType::LoadBaseAddr);
        set.insert(TagType::LoadBaseAddr);
        assert_eq!(set.len(), 4);
        for (current, next) in set.iter().zip(set.iter().skip(1)) {
            assert!(current < next);
        }
        println!("{:#?}", set);
    }

    /// Tests for equality when one type is u32 and the other the enum representation.
    #[test]
    fn test_partial_eq_u32() {
        assert_eq!(21, TagType::LoadBaseAddr);
        assert_eq!(TagType::LoadBaseAddr, 21);
        assert_eq!(21, TagTypeId(21));
        assert_eq!(TagTypeId(21), 21);
        assert_eq!(42, TagType::Custom(42));
        assert_eq!(TagType::Custom(42), 42);
    }

    /// Tests the construction of [`TagTypeId`] from primitive `u32` values.
    #[test]
    #[allow(non_snake_case)]
    fn test_TagTypeId() {
        assert_eq!(size_of::<TagTypeId>(), size_of::<u32>());
        assert_eq!(align_of::<TagTypeId>(), align_of::<u32>());

        for i in 0..50_u32 {
            let val: TagTypeId = i.into();
            let val2: TagType = val.into();
            assert_eq!(val, val2);
        }

        let tag_custom: u32 = 0x1337;
        let tag_custom: TagTypeId = tag_custom.into();
        let tag_custom: TagType = tag_custom.into();
        matches!(tag_custom, TagType::Custom(0x1337));
    }

    /// Tests the construction of [`TagTypeId`] from primitive `u32` values for
    /// specified and custom tags.
    #[test]
    #[allow(non_snake_case)]
    fn test_from_and_to_tag_type_id() {
        for i in 0..1_000 {
            let tag_type_id = TagTypeId::new(i);
            let tag_type_from_id = TagType::from(tag_type_id);
            let tag_type_from_u16 = TagType::from(i);
            assert_eq!(tag_type_from_id, tag_type_from_u16)
        }
    }
}