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}