From bd73c24374237af9b46bf4d5979b820e002b0fc6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 24 Dec 2022 12:00:05 +0100 Subject: [PATCH 1/6] multiboot2: add Tag::cast_tag to prevent code duplication --- multiboot2/src/lib.rs | 24 ++++++++++++------------ multiboot2/src/tag_type.rs | 7 +++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index f083d9ca..581934b6 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -225,7 +225,7 @@ impl BootInformation { /// Search for the Memory map tag. pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> { self.get_tag(TagType::Mmap) - .map(|tag| unsafe { &*(tag as *const Tag as *const MemoryMapTag) }) + .map(|tag| tag.cast_tag::()) } /// Get an iterator of all module tags. @@ -236,13 +236,13 @@ impl BootInformation { /// Search for the BootLoader name tag. pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> { self.get_tag(TagType::BootLoaderName) - .map(|tag| unsafe { &*(tag as *const Tag as *const BootLoaderNameTag) }) + .map(|tag| tag.cast_tag::()) } /// Search for the Command line tag. pub fn command_line_tag(&self) -> Option<&CommandLineTag> { self.get_tag(TagType::Cmdline) - .map(|tag| unsafe { &*(tag as *const Tag as *const CommandLineTag) }) + .map(|tag| tag.cast_tag::()) } /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the @@ -255,25 +255,25 @@ impl BootInformation { /// Search for the EFI 32-bit SDT tag. pub fn efi_sdt_32_tag(&self) -> Option<&EFISdt32> { self.get_tag(TagType::Efi32) - .map(|tag| unsafe { &*(tag as *const Tag as *const EFISdt32) }) + .map(|tag| tag.cast_tag::()) } /// Search for the EFI 64-bit SDT tag. pub fn efi_sdt_64_tag(&self) -> Option<&EFISdt64> { self.get_tag(TagType::Efi64) - .map(|tag| unsafe { &*(tag as *const Tag as *const EFISdt64) }) + .map(|tag| tag.cast_tag::()) } /// Search for the (ACPI 1.0) RSDP tag. pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> { self.get_tag(TagType::AcpiV1) - .map(|tag| unsafe { &*(tag as *const Tag as *const RsdpV1Tag) }) + .map(|tag| tag.cast_tag::()) } /// Search for the (ACPI 2.0 or later) RSDP tag. pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> { self.get_tag(TagType::AcpiV2) - .map(|tag| unsafe { &*(tag as *const Tag as *const RsdpV2Tag) }) + .map(|tag| tag.cast_tag::()) } /// Search for the EFI Memory map tag, if the boot services were exited. @@ -287,32 +287,32 @@ impl BootInformation { Some(_tag) => None, None => self .get_tag(TagType::EfiMmap) - .map(|tag| unsafe { &*(tag as *const Tag as *const EFIMemoryMapTag) }), + .map(|tag| tag.cast_tag::()), } } /// Search for the EFI 32-bit image handle pointer. pub fn efi_32_ih(&self) -> Option<&EFIImageHandle32> { self.get_tag(TagType::Efi32Ih) - .map(|tag| unsafe { &*(tag as *const Tag as *const EFIImageHandle32) }) + .map(|tag| tag.cast_tag::()) } /// Search for the EFI 64-bit image handle pointer. pub fn efi_64_ih(&self) -> Option<&EFIImageHandle64> { self.get_tag(TagType::Efi64Ih) - .map(|tag| unsafe { &*(tag as *const Tag as *const EFIImageHandle64) }) + .map(|tag| tag.cast_tag::()) } /// Search for the Image Load Base Physical Address. pub fn load_base_addr(&self) -> Option<&ImageLoadPhysAddr> { self.get_tag(TagType::LoadBaseAddr) - .map(|tag| unsafe { &*(tag as *const Tag as *const ImageLoadPhysAddr) }) + .map(|tag| tag.cast_tag::()) } /// Search for the VBE information tag. pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> { self.get_tag(TagType::Vbe) - .map(|tag| unsafe { &*(tag as *const Tag as *const VBEInfoTag) }) + .map(|tag| tag.cast_tag::()) } fn get(&self) -> &BootInformationInner { diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 3f2e12a0..f7d3d969 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -114,6 +114,13 @@ pub struct Tag { // tag specific fields } +impl Tag { + /// Casts the base tag to the specific tag type. + pub fn cast_tag<'a, T>(&self) -> &'a T { + unsafe { &*(self as *const Tag as *const T) } + } +} + impl Debug for Tag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("Tag") From 39435b81dadaff27466701cbe5753a9d6e41bac5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 24 Dec 2022 12:06:29 +0100 Subject: [PATCH 2/6] multiboot2: split TagType into TagType and TagTypeId This change allows to get custom tag types. --- multiboot2/Changelog.md | 15 +- multiboot2/src/boot_loader_name.rs | 6 +- multiboot2/src/command_line.rs | 6 +- multiboot2/src/efi.rs | 10 +- multiboot2/src/elf_sections.rs | 3 +- multiboot2/src/image_load_addr.rs | 4 +- multiboot2/src/lib.rs | 12 +- multiboot2/src/memory_map.rs | 6 +- multiboot2/src/module.rs | 5 +- multiboot2/src/rsdp.rs | 6 +- multiboot2/src/tag_type.rs | 419 ++++++++++++++++++++++------- multiboot2/src/vbe_info.rs | 4 +- 12 files changed, 366 insertions(+), 130 deletions(-) diff --git a/multiboot2/Changelog.md b/multiboot2/Changelog.md index aad014ad..02938553 100644 --- a/multiboot2/Changelog.md +++ b/multiboot2/Changelog.md @@ -1,15 +1,24 @@ # CHANGELOG for crate `multiboot2` -## Unreleased +## 0.15.0 (2023-03-XX) - MSRV is 1.56.1 -- **BREAKING** fixed lifetime issues: `VBEInfoTag` is no longer static +- **BREAKING** fixed lifetime issues: `VBEInfoTag` is no longer `&static` +- **BREAKING:** `TagType` is now split into `TagTypeId` and `TagType` + - `TagTypeId` is a binary-compatible form of a Multiboot2 tag id + - `TagType` is a higher-level abstraction for either specified or custom tags + but not ABI compatible. - fixed another internal lifetime issue - `BootInformation::framebuffer_tag()` now returns `Option>` instead of `Option` which prevents a possible panic. If the `--unstable` feature is used, `UnknownFramebufferType` implements `core::error::Error`. - Fixed misleading documentation of the `BootInformation::efi_memory_map_tag` - tag. +- `BootInformation` now publicly exports the `get_tag` function allowing you to + work with custom tags. An example is given in the function documentation. + (check docs.rs). There is also a small unit test that you can use to learn + from. +- There exists a seamless integration between `u32`, `TagType`, and `TagTypeId` + via `From` and `PartialEq`-implementations. ## 0.14.2 (2023-03-17) - documentation fixes diff --git a/multiboot2/src/boot_loader_name.rs b/multiboot2/src/boot_loader_name.rs index c9fc73bd..ea2cb07c 100644 --- a/multiboot2/src/boot_loader_name.rs +++ b/multiboot2/src/boot_loader_name.rs @@ -1,4 +1,4 @@ -use crate::TagType; +use crate::TagTypeId; use core::str::Utf8Error; /// This tag contains the name of the bootloader that is booting the kernel. @@ -8,7 +8,7 @@ use core::str::Utf8Error; #[derive(Clone, Copy, Debug)] #[repr(C, packed)] // only repr(C) would add unwanted padding before first_section pub struct BootLoaderNameTag { - typ: TagType, + typ: TagTypeId, size: u32, /// Null-terminated UTF-8 string string: u8, @@ -47,7 +47,7 @@ mod tests { // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string let size = (4 + 4 + MSG.as_bytes().len() + 1) as u32; [ - &((TagType::BootLoaderName as u32).to_ne_bytes()), + &((TagType::BootLoaderName.val()).to_ne_bytes()), &size.to_ne_bytes(), MSG.as_bytes(), // Null Byte diff --git a/multiboot2/src/command_line.rs b/multiboot2/src/command_line.rs index 80ddf70b..978cbe0a 100644 --- a/multiboot2/src/command_line.rs +++ b/multiboot2/src/command_line.rs @@ -1,6 +1,6 @@ //! Module for [CommandLineTag]. -use crate::TagType; +use crate::TagTypeId; use core::mem; use core::slice; use core::str; @@ -12,7 +12,7 @@ use core::str; #[derive(Clone, Copy, Debug)] #[repr(C, packed)] // only repr(C) would add unwanted padding before first_section pub struct CommandLineTag { - typ: TagType, + typ: TagTypeId, size: u32, /// Null-terminated UTF-8 string string: u8, @@ -51,7 +51,7 @@ mod tests { // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string let size = (4 + 4 + MSG.as_bytes().len() + 1) as u32; [ - &((TagType::Cmdline as u32).to_ne_bytes()), + &((TagType::Cmdline.val()).to_ne_bytes()), &size.to_ne_bytes(), MSG.as_bytes(), // Null Byte diff --git a/multiboot2/src/efi.rs b/multiboot2/src/efi.rs index d22b323c..4a239f9d 100644 --- a/multiboot2/src/efi.rs +++ b/multiboot2/src/efi.rs @@ -1,12 +1,12 @@ //! All MBI tags related to (U)EFI. -use crate::TagType; +use crate::TagTypeId; /// EFI system table in 32 bit mode #[derive(Clone, Copy, Debug)] #[repr(C, packed)] // only repr(C) would add unwanted padding before first_section pub struct EFISdt32 { - typ: TagType, + typ: TagTypeId, size: u32, pointer: u32, } @@ -22,7 +22,7 @@ impl EFISdt32 { #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct EFISdt64 { - typ: TagType, + typ: TagTypeId, size: u32, pointer: u64, } @@ -38,7 +38,7 @@ impl EFISdt64 { #[derive(Debug)] #[repr(C)] pub struct EFIImageHandle32 { - typ: TagType, + typ: TagTypeId, size: u32, pointer: u32, } @@ -54,7 +54,7 @@ impl EFIImageHandle32 { #[derive(Debug)] #[repr(C)] pub struct EFIImageHandle64 { - typ: TagType, + typ: TagTypeId, size: u32, pointer: u64, } diff --git a/multiboot2/src/elf_sections.rs b/multiboot2/src/elf_sections.rs index c89a37e4..a628079a 100644 --- a/multiboot2/src/elf_sections.rs +++ b/multiboot2/src/elf_sections.rs @@ -1,4 +1,5 @@ use crate::tag_type::Tag; +use crate::TagType; use core::fmt::{Debug, Formatter}; /// This tag contains section header table from an ELF kernel. @@ -11,7 +12,7 @@ pub struct ElfSectionsTag { } pub unsafe fn elf_sections_tag(tag: &Tag, offset: usize) -> ElfSectionsTag { - assert_eq!(9, tag.typ); + assert_eq!(TagType::ElfSections.val(), tag.typ); let es = ElfSectionsTag { inner: (tag as *const Tag).offset(1) as *const ElfSectionsTagInner, offset, diff --git a/multiboot2/src/image_load_addr.rs b/multiboot2/src/image_load_addr.rs index ab1d432c..a5300299 100644 --- a/multiboot2/src/image_load_addr.rs +++ b/multiboot2/src/image_load_addr.rs @@ -1,11 +1,11 @@ -use crate::TagType; +use crate::TagTypeId; /// If the image has relocatable header tag, this tag contains the image's /// base physical address. #[derive(Debug)] #[repr(C)] pub struct ImageLoadPhysAddr { - typ: TagType, + typ: TagTypeId, size: u32, load_base_addr: u32, } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 581934b6..c127754c 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -55,8 +55,8 @@ pub use memory_map::{ }; pub use module::{ModuleIter, ModuleTag}; pub use rsdp::{RsdpV1Tag, RsdpV2Tag}; -pub use tag_type::TagType; -use tag_type::{Tag, TagIter}; +pub use tag_type::{Tag, TagType, TagTypeId}; +use tag_type::TagIter; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, @@ -330,16 +330,16 @@ impl BootInformation { impl BootInformationInner { fn has_valid_end_tag(&self) -> bool { - const END_TAG: Tag = Tag { - typ: TagType::End, + let end_tag_prototype: Tag = Tag { + typ: TagType::End.into(), size: 8, }; let self_ptr = self as *const _; - let end_tag_addr = self_ptr as usize + (self.total_size - END_TAG.size) as usize; + let end_tag_addr = self_ptr as usize + (self.total_size - end_tag_prototype.size) as usize; let end_tag = unsafe { &*(end_tag_addr as *const Tag) }; - end_tag.typ == END_TAG.typ && end_tag.size == END_TAG.size + end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size } } diff --git a/multiboot2/src/memory_map.rs b/multiboot2/src/memory_map.rs index e8972947..20c1bb39 100644 --- a/multiboot2/src/memory_map.rs +++ b/multiboot2/src/memory_map.rs @@ -1,4 +1,4 @@ -use crate::TagType; +use crate::TagTypeId; use core::marker::PhantomData; /// This tag provides an initial host memory map. @@ -14,7 +14,7 @@ use core::marker::PhantomData; #[derive(Debug)] #[repr(C)] pub struct MemoryMapTag { - typ: TagType, + typ: TagTypeId, size: u32, entry_size: u32, entry_version: u32, @@ -126,7 +126,7 @@ impl<'a> Iterator for MemoryAreaIter<'a> { #[derive(Debug)] #[repr(C)] pub struct EFIMemoryMapTag { - typ: TagType, + typ: TagTypeId, size: u32, desc_size: u32, desc_version: u32, diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index aeea785f..5d5cf53f 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -1,4 +1,5 @@ use crate::tag_type::{Tag, TagIter, TagType}; +use crate::TagTypeId; use core::fmt::{Debug, Formatter}; use core::str::Utf8Error; @@ -7,7 +8,7 @@ use core::str::Utf8Error; #[derive(Clone, Copy)] #[repr(C, packed)] // only repr(C) would add unwanted padding near name_byte. pub struct ModuleTag { - typ: TagType, + typ: TagTypeId, size: u32, mod_start: u32, mod_end: u32, @@ -102,7 +103,7 @@ mod tests { // 4 bytes mod_start + 4 bytes mod_end let size = (4 + 4 + 4 + 4 + MSG.as_bytes().len() + 1) as u32; [ - &((TagType::Module as u32).to_ne_bytes()), + &((TagType::Module.val()).to_ne_bytes()), &size.to_ne_bytes(), &0_u32.to_ne_bytes(), &0_u32.to_ne_bytes(), diff --git a/multiboot2/src/rsdp.rs b/multiboot2/src/rsdp.rs index 9188f204..bc7ef1ef 100644 --- a/multiboot2/src/rsdp.rs +++ b/multiboot2/src/rsdp.rs @@ -8,7 +8,7 @@ //! //! Even though the bootloader should give the address of the real RSDP/XSDT, the checksum and //! signature should be manually verified. -use crate::TagType; +use crate::TagTypeId; use core::slice; use core::str; use core::str::Utf8Error; @@ -19,7 +19,7 @@ const RSDPV1_LENGTH: usize = 20; #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct RsdpV1Tag { - typ: TagType, + typ: TagTypeId, size: u32, signature: [u8; 8], checksum: u8, @@ -66,7 +66,7 @@ impl RsdpV1Tag { #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct RsdpV2Tag { - typ: TagType, + typ: TagTypeId, size: u32, signature: [u8; 8], checksum: u8, diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index f7d3d969..6aae7c6f 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -1,115 +1,295 @@ -//! Module for [`TagType`]. +//! Module for the basic Multiboot2 tag and corresponding tag types. +//! +//! The relevant exports of this module are: +//! - [`TagTypeId`] +//! - [`TagType`] +//! - [`Tag`] use core::fmt::{Debug, Formatter}; use core::hash::Hash; use core::marker::PhantomData; -/// Possible types of a Tag in the Multiboot2 Information Structure (MBI), therefore the value -/// of the the `typ` property. The names and values are taken from the example C code -/// at the bottom of the Multiboot2 specification. -#[repr(u32)] -#[derive(Copy, Clone, Debug, Eq, Ord, PartialOrd, PartialEq, 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`]. +#[repr(transparent)] +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Hash)] +pub struct TagTypeId(u32); + +impl TagTypeId { + /// Constructor. + pub fn new(val: u32) -> Self { + Self(val) + } +} + +/// 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, PartialOrd, PartialEq, Eq, Ord, Hash)] pub enum TagType { - /// Marks the end of the tags. - End = 0, - /// 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 = 1, - /// Name of the bootloader, e.g. 'GRUB 2.04-1ubuntu44.2' - BootLoaderName = 2, - /// Additional Multiboot modules, which are BLOBs provided in memory. For example an initial - /// ram disk with essential drivers. - Module = 3, - /// ‘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. + /// 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, /* 1 */ + /// 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 = 4, - /// 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 + /// 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 = 5, - /// 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 = 6, - /// Contains the VBE control information returned by the VBE Function 00h and VBE mode - /// information returned by the VBE Function 01h, 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 = 7, - /// Framebuffer. - Framebuffer = 8, - /// 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 = 9, - /// APM table. See Advanced Power Management (APM) BIOS Interface Specification, for more - /// information. - Apm = 10, - /// This tag contains pointer to i386 EFI system table. - Efi32 = 11, - /// This tag contains pointer to amd64 EFI system table. - Efi64 = 12, - /// This tag contains a copy of SMBIOS tables as well as their version. - Smbios = 13, - /// Also called "AcpiOld" in other multiboot2 implementations. - AcpiV1 = 14, - /// Refers to version 2 and later of Acpi. + 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 = 15, - /// 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 + 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 = 16, - /// 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 = 17, - /// This tag indicates ExitBootServices wasn't called. - EfiBs = 18, - /// This tag contains pointer to EFI i386 image handle. Usually it is boot loader image handle. - Efi32Ih = 19, - /// This tag contains pointer to EFI amd64 image handle. Usually it is boot loader image handle. - Efi64Ih = 20, - /// 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 = 21, + 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 for TagTypeId { + fn from(value: u32) -> Self { + // SAFETY: the type has repr(transparent) + unsafe { core::mem::transmute(value) } + } + } + + impl From for u32 { + fn from(value: TagTypeId) -> Self { + value.0 as _ + } + } + + impl From 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 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, + } + } + } } -// each compare/equal direction must be implemented manually -impl PartialEq for TagType { - fn eq(&self, other: &u32) -> bool { - *self as u32 == *other +/// `From`-implementations for conversions between [´TagTypeId´] and [´TagType´]. +mod intermediate_conversion_impls { + use super::*; + + impl From for TagType { + fn from(value: TagTypeId) -> Self { + let value = u32::from(value); + TagType::from(value) + } + } + + impl From for TagTypeId { + fn from(value: TagType) -> Self { + let value = u32::from(value); + TagTypeId::from(value) + } } } -// each compare/equal direction must be implemented manually -impl PartialEq for u32 { - fn eq(&self, other: &TagType) -> bool { - *self == *other as u32 +/// 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 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 for TagTypeId { + fn eq(&self, other: &TagType) -> bool { + other.eq(self) + } + } + + impl PartialEq for TagTypeId { + fn eq(&self, other: &u32) -> bool { + let this = u32::from(*self); + this == *other + } + } + + impl PartialEq for u32 { + fn eq(&self, other: &TagTypeId) -> bool { + other.eq(self) + } + } + + impl PartialEq for TagType { + fn eq(&self, other: &u32) -> bool { + let this = u32::from(*self); + this == *other + } + } + + impl PartialEq for u32 { + fn eq(&self, other: &TagType) -> bool { + other.eq(self) + } } } -/// All tags that could passed via the Multiboot2 information structure to a payload/program/kernel. -/// Better not confuse this with the Multiboot2 header tags. They are something different. +/// Common base structure for all tags that can be passed via the Multiboot2 +/// information structure (MBI) to a Multiboot2 payload/program/kernel. +/// +/// Do not confuse them with the Multiboot2 header tags. They are something +/// different. #[derive(Clone, Copy)] #[repr(C)] pub struct Tag { - // u32 value - pub typ: TagType, + pub typ: TagTypeId, // u32 pub size: u32, // tag specific fields } @@ -123,11 +303,18 @@ impl Tag { impl Debug for Tag { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Tag") - .field("typ", &self.typ) - .field("typ (numeric)", &(self.typ as u32)) - .field("size", &(self.size)) - .finish() + let tag_type = TagType::from(self.typ); + + let mut debug = f.debug_struct("Tag"); + debug.field("typ", &tag_type); + + if !matches!(tag_type, TagType::Custom(_)) { + debug.field("typ (numeric)", &(u32::from(self.typ))); + } + + debug.field("size", &(self.size)); + + debug.finish() } } @@ -152,7 +339,8 @@ impl<'a> Iterator for TagIter<'a> { fn next(&mut self) -> Option<&'a Tag> { match unsafe { &*self.current } { &Tag { - typ: TagType::End, + // END-Tag + typ: TagTypeId(0), size: 8, } => None, // end tag tag => { @@ -170,6 +358,7 @@ impl<'a> Iterator for TagIter<'a> { #[cfg(test)] mod tests { use super::*; + use std::mem::{align_of, size_of}; #[test] fn test_hashset() { @@ -203,5 +392,41 @@ mod tests { 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::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + 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) + } } } diff --git a/multiboot2/src/vbe_info.rs b/multiboot2/src/vbe_info.rs index d441137a..64f53396 100644 --- a/multiboot2/src/vbe_info.rs +++ b/multiboot2/src/vbe_info.rs @@ -1,4 +1,4 @@ -use crate::TagType; +use crate::TagTypeId; use core::fmt; /// This tag contains VBE metadata, VBE controller information returned by the @@ -6,7 +6,7 @@ use core::fmt; #[derive(Debug, Copy, Clone)] #[repr(C, packed)] pub struct VBEInfoTag { - typ: TagType, + typ: TagTypeId, length: u32, /// Indicates current video mode in the format specified in VBE 3.0. From 07e9a250020b4d2b8e2916788a2faa0d66231fbb Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Fri, 23 Dec 2022 23:44:49 +0100 Subject: [PATCH 3/6] multiboot2: expose get_tag publicly to allow custom tags --- multiboot2/src/lib.rs | 39 ++++++++++++++++++++++++++++++++++++-- multiboot2/src/module.rs | 2 +- multiboot2/src/tag_type.rs | 2 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index c127754c..cdf4bd87 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -55,8 +55,8 @@ pub use memory_map::{ }; pub use module::{ModuleIter, ModuleTag}; pub use rsdp::{RsdpV1Tag, RsdpV2Tag}; -pub use tag_type::{Tag, TagType, TagTypeId}; use tag_type::TagIter; +pub use tag_type::{Tag, TagType, TagTypeId}; pub use vbe_info::{ VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag, VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes, @@ -319,7 +319,42 @@ impl BootInformation { unsafe { &*self.inner } } - fn get_tag(&self, typ: TagType) -> Option<&Tag> { + /// Public getter to find any Multiboot tag by its type, including + /// specified and custom ones. + /// + /// # Specified or Custom Tags + /// The Multiboot2 specification specifies a list of tags, see [`TagType`]. + /// However, it doesn't forbid to use custom tags. Because of this, there + /// exists the [`TagType`] abstraction. It is recommended to use this + /// getter only for custom tags. For specified tags, use getters, such as + /// [`Self::efi_64_ih`]. + /// + /// ## Use Custom Tags + /// The following example shows how you may use this interface to parse custom tags from + /// the MBI. + /// + /// ```ignore + /// use multiboot2::TagTypeId; + /// #[repr(C, align(8))] + /// struct CustomTag { + /// // new type from the lib: has repr(u32) + /// tag: TagTypeId, + /// size: u32, + /// // begin of inline string + /// name: u8, + /// } + /// + /// let tag = bi + /// // this function is now public! + /// .get_tag(0x1337.into()) + /// .unwrap() + /// // type definition from end user; must be `Sized`! + /// .cast_tag::(); + /// let name = &tag.name as *const u8 as *const c_char; + /// let str = unsafe { CStr::from_ptr(name).to_str().unwrap() }; + /// assert_eq!(str, "name"); + /// ``` + pub fn get_tag(&self, typ: TagType) -> Option<&Tag> { self.tags().find(|tag| tag.typ == typ) } diff --git a/multiboot2/src/module.rs b/multiboot2/src/module.rs index 5d5cf53f..555cf89a 100644 --- a/multiboot2/src/module.rs +++ b/multiboot2/src/module.rs @@ -76,7 +76,7 @@ impl<'a> Iterator for ModuleIter<'a> { fn next(&mut self) -> Option<&'a ModuleTag> { self.iter - .find(|x| x.typ == TagType::Module) + .find(|tag| tag.typ == TagType::Module) .map(|tag| unsafe { &*(tag as *const Tag as *const ModuleTag) }) } } diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 6aae7c6f..49953b84 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -291,7 +291,7 @@ mod partial_eq_impls { pub struct Tag { pub typ: TagTypeId, // u32 pub size: u32, - // tag specific fields + // additional, tag specific fields } impl Tag { From 73e97a00aff192a9f6ae51fbf7b1e314b2f1954e Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 27 Nov 2022 15:20:37 +0100 Subject: [PATCH 4/6] multiboot2: add test for custom tags --- multiboot2/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index cdf4bd87..f9752579 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -483,6 +483,7 @@ impl Reader { #[cfg(test)] mod tests { use super::*; + use std::{mem, slice}; #[test] fn no_tags() { @@ -1492,4 +1493,70 @@ mod tests { fn consumer(_e: E) {} consumer(MbiLoadError::IllegalAddress) } + + fn custom_tag() { + const CUSTOM_TAG_ID: u32 = 0x1337; + + #[repr(C, align(8))] + struct Bytes([u8; 32]); + let bytes: Bytes = Bytes([ + 32, + 0, + 0, + 0, // total_size + 0, + 0, + 0, + 0, // reserved + // my custom tag + CUSTOM_TAG_ID.to_ne_bytes()[0], + CUSTOM_TAG_ID.to_ne_bytes()[1], + CUSTOM_TAG_ID.to_ne_bytes()[2], + CUSTOM_TAG_ID.to_ne_bytes()[3], + 13, + 0, + 0, + 0, // tag size + 110, + 97, + 109, + 101, // ASCII string 'name' + 0, + 0, + 0, + 0, // null byte + padding + 0, + 0, + 0, + 0, // end tag type + 8, + 0, + 0, + 0, // end tag size + ]); + let addr = bytes.0.as_ptr() as usize; + let bi = unsafe { load(addr) }; + let bi = bi.unwrap(); + assert_eq!(addr, bi.start_address()); + assert_eq!(addr + bytes.0.len(), bi.end_address()); + assert_eq!(bytes.0.len(), bi.total_size()); + + #[repr(C, align(8))] + struct CustomTag { + tag: TagTypeId, + size: u32, + name: u8, + } + + let tag = bi + .get_tag(CUSTOM_TAG_ID.into()) + .unwrap() + .cast_tag::(); + + // strlen without null byte + let strlen = tag.size as usize - mem::size_of::(); + let bytes = unsafe { slice::from_raw_parts((&tag.name) as *const u8, strlen) }; + let name = core::str::from_utf8(bytes).unwrap(); + assert_eq!(name, "name"); + } } From ff60f052ca6c99880bd22db67c2be944a1c4948f Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 24 Dec 2022 00:29:00 +0100 Subject: [PATCH 5/6] multiboot2: get_tag consumes Into --- multiboot2/src/lib.rs | 71 +++++++++++++++++++++++++++++++++----- multiboot2/src/tag_type.rs | 2 +- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index f9752579..e1a6800e 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -322,6 +322,8 @@ impl BootInformation { /// Public getter to find any Multiboot tag by its type, including /// specified and custom ones. /// + /// The parameter can be of type `u32`, [`TagType`], or [`TagTypeId`]. + /// /// # Specified or Custom Tags /// The Multiboot2 specification specifies a list of tags, see [`TagType`]. /// However, it doesn't forbid to use custom tags. Because of this, there @@ -331,10 +333,13 @@ impl BootInformation { /// /// ## Use Custom Tags /// The following example shows how you may use this interface to parse custom tags from - /// the MBI. + /// the MBI. Custom tags must be `Sized`. Hence, they are not allowed to contain a field such + /// as `name: [u8]`. /// - /// ```ignore + /// ```rust + /// use std::ffi::{c_char, CStr}; /// use multiboot2::TagTypeId; + /// /// #[repr(C, align(8))] /// struct CustomTag { /// // new type from the lib: has repr(u32) @@ -345,8 +350,7 @@ impl BootInformation { /// } /// /// let tag = bi - /// // this function is now public! - /// .get_tag(0x1337.into()) + /// .get_tag(0x1337) /// .unwrap() /// // type definition from end user; must be `Sized`! /// .cast_tag::(); @@ -354,7 +358,8 @@ impl BootInformation { /// let str = unsafe { CStr::from_ptr(name).to_str().unwrap() }; /// assert_eq!(str, "name"); /// ``` - pub fn get_tag(&self, typ: TagType) -> Option<&Tag> { + pub fn get_tag(&self, typ: impl Into) -> Option<&Tag> { + let typ = typ.into(); self.tags().find(|tag| tag.typ == typ) } @@ -1548,10 +1553,7 @@ mod tests { name: u8, } - let tag = bi - .get_tag(CUSTOM_TAG_ID.into()) - .unwrap() - .cast_tag::(); + let tag = bi.get_tag(CUSTOM_TAG_ID).unwrap().cast_tag::(); // strlen without null byte let strlen = tag.size as usize - mem::size_of::(); @@ -1559,4 +1561,55 @@ mod tests { let name = core::str::from_utf8(bytes).unwrap(); assert_eq!(name, "name"); } + + /// Tests that `get_tag` can consume multiple types that implement `Into` + #[test] + fn get_tag_into_variants() { + #[repr(C, align(8))] + struct Bytes([u8; 32]); + let bytes: Bytes = Bytes([ + 32, + 0, + 0, + 0, // total_size + 0, + 0, + 0, + 0, // reserved + TagType::Cmdline.val().to_ne_bytes()[0], + TagType::Cmdline.val().to_ne_bytes()[1], + TagType::Cmdline.val().to_ne_bytes()[2], + TagType::Cmdline.val().to_ne_bytes()[3], + 13, + 0, + 0, + 0, // tag size + 110, + 97, + 109, + 101, // ASCII string 'name' + 0, + 0, + 0, + 0, // null byte + padding + 0, + 0, + 0, + 0, // end tag type + 8, + 0, + 0, + 0, // end tag size + ]); + + let addr = bytes.0.as_ptr() as usize; + let bi = unsafe { load(addr) }; + let bi = bi.unwrap(); + + let _tag = bi.get_tag(TagType::Cmdline).unwrap(); + + let _tag = bi.get_tag(1).unwrap(); + + let _tag = bi.get_tag(TagTypeId::new(1)).unwrap(); + } } diff --git a/multiboot2/src/tag_type.rs b/multiboot2/src/tag_type.rs index 49953b84..3d5132c3 100644 --- a/multiboot2/src/tag_type.rs +++ b/multiboot2/src/tag_type.rs @@ -35,7 +35,7 @@ pub enum TagType { /// 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, /* 1 */ + 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 From cdfc1c72924f6ef71013ed20ce3692d0b71078c1 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Fri, 17 Mar 2023 17:55:19 +0100 Subject: [PATCH 6/6] multiboot2: simplify usage of cast_tag::() --- multiboot2/src/lib.rs | 70 +++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index e1a6800e..38af02bc 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -218,14 +218,13 @@ impl BootInformation { /// Search for the ELF Sections tag. pub fn elf_sections_tag(&self) -> Option { - self.get_tag(TagType::ElfSections) + self.get_tag::(TagType::ElfSections) .map(|tag| unsafe { elf_sections::elf_sections_tag(tag, self.offset) }) } /// Search for the Memory map tag. pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> { - self.get_tag(TagType::Mmap) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Mmap) } /// Get an iterator of all module tags. @@ -235,45 +234,39 @@ impl BootInformation { /// Search for the BootLoader name tag. pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> { - self.get_tag(TagType::BootLoaderName) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::BootLoaderName) } /// Search for the Command line tag. pub fn command_line_tag(&self) -> Option<&CommandLineTag> { - self.get_tag(TagType::Cmdline) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Cmdline) } /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the /// framebuffer type is unknown, while the framebuffer tag is present. pub fn framebuffer_tag(&self) -> Option> { - self.get_tag(TagType::Framebuffer) + self.get_tag::(TagType::Framebuffer) .map(framebuffer::framebuffer_tag) } /// Search for the EFI 32-bit SDT tag. pub fn efi_sdt_32_tag(&self) -> Option<&EFISdt32> { - self.get_tag(TagType::Efi32) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Efi32) } /// Search for the EFI 64-bit SDT tag. pub fn efi_sdt_64_tag(&self) -> Option<&EFISdt64> { - self.get_tag(TagType::Efi64) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Efi64) } /// Search for the (ACPI 1.0) RSDP tag. pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> { - self.get_tag(TagType::AcpiV1) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::AcpiV1) } /// Search for the (ACPI 2.0 or later) RSDP tag. pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> { - self.get_tag(TagType::AcpiV2) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::AcpiV2) } /// Search for the EFI Memory map tag, if the boot services were exited. @@ -283,36 +276,30 @@ impl BootInformation { pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> { // If the EFIBootServicesNotExited is present, then we should not use // the memory map, as it could still be in use. - match self.get_tag(TagType::EfiBs) { + match self.get_tag::(TagType::EfiBs) { Some(_tag) => None, - None => self - .get_tag(TagType::EfiMmap) - .map(|tag| tag.cast_tag::()), + None => self.get_tag::(TagType::EfiMmap), } } /// Search for the EFI 32-bit image handle pointer. pub fn efi_32_ih(&self) -> Option<&EFIImageHandle32> { - self.get_tag(TagType::Efi32Ih) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Efi32Ih) } /// Search for the EFI 64-bit image handle pointer. pub fn efi_64_ih(&self) -> Option<&EFIImageHandle64> { - self.get_tag(TagType::Efi64Ih) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Efi64Ih) } /// Search for the Image Load Base Physical Address. pub fn load_base_addr(&self) -> Option<&ImageLoadPhysAddr> { - self.get_tag(TagType::LoadBaseAddr) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::LoadBaseAddr) } /// Search for the VBE information tag. pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> { - self.get_tag(TagType::Vbe) - .map(|tag| tag.cast_tag::()) + self.get_tag::(TagType::Vbe) } fn get(&self) -> &BootInformationInner { @@ -336,7 +323,8 @@ impl BootInformation { /// the MBI. Custom tags must be `Sized`. Hence, they are not allowed to contain a field such /// as `name: [u8]`. /// - /// ```rust + /// **Belows example needs Rust 1.64 or newer because of std::ffi imports!** + /// ```ignore /// use std::ffi::{c_char, CStr}; /// use multiboot2::TagTypeId; /// @@ -349,18 +337,21 @@ impl BootInformation { /// name: u8, /// } /// - /// let tag = bi - /// .get_tag(0x1337) - /// .unwrap() + /// let mbi = unsafe { multiboot2::load(0xdeadbeef).unwrap() }; + /// + /// let tag = mbi /// // type definition from end user; must be `Sized`! - /// .cast_tag::(); + /// .get_tag::(0x1337) + /// .unwrap(); /// let name = &tag.name as *const u8 as *const c_char; /// let str = unsafe { CStr::from_ptr(name).to_str().unwrap() }; /// assert_eq!(str, "name"); /// ``` - pub fn get_tag(&self, typ: impl Into) -> Option<&Tag> { + pub fn get_tag>(&self, typ: TagType) -> Option<&Tag> { let typ = typ.into(); - self.tags().find(|tag| tag.typ == typ) + self.tags() + .find(|tag| tag.typ == typ) + .map(|tag| tag.cast_tag::()) } fn tags(&self) -> TagIter { @@ -1499,6 +1490,7 @@ mod tests { consumer(MbiLoadError::IllegalAddress) } + #[test] fn custom_tag() { const CUSTOM_TAG_ID: u32 = 0x1337; @@ -1553,7 +1545,7 @@ mod tests { name: u8, } - let tag = bi.get_tag(CUSTOM_TAG_ID).unwrap().cast_tag::(); + let tag = bi.get_tag::(CUSTOM_TAG_ID).unwrap(); // strlen without null byte let strlen = tag.size as usize - mem::size_of::(); @@ -1606,10 +1598,10 @@ mod tests { let bi = unsafe { load(addr) }; let bi = bi.unwrap(); - let _tag = bi.get_tag(TagType::Cmdline).unwrap(); + let _tag = bi.get_tag::(TagType::Cmdline).unwrap(); - let _tag = bi.get_tag(1).unwrap(); + let _tag = bi.get_tag::(1).unwrap(); - let _tag = bi.get_tag(TagTypeId::new(1)).unwrap(); + let _tag = bi.get_tag::(TagTypeId::new(1)).unwrap(); } }