multiboot2/
boot_loader_name.rs

1//! Module for [`BootLoaderNameTag`].
2
3use crate::tag::TagHeader;
4use crate::{StringError, TagType, parse_slice_as_string};
5use core::fmt::{Debug, Formatter};
6use core::mem;
7use multiboot2_common::{MaybeDynSized, Tag};
8#[cfg(feature = "builder")]
9use {alloc::boxed::Box, multiboot2_common::new_boxed};
10
11/// The bootloader name tag.
12#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(C, align(8))]
14pub struct BootLoaderNameTag {
15    header: TagHeader,
16    /// Null-terminated UTF-8 string
17    name: [u8],
18}
19
20impl BootLoaderNameTag {
21    /// Constructs a new tag.
22    #[cfg(feature = "builder")]
23    #[must_use]
24    pub fn new(name: &str) -> Box<Self> {
25        let header = TagHeader::new(Self::ID, 0);
26        let bytes = name.as_bytes();
27        if bytes.ends_with(&[0]) {
28            new_boxed(header, &[bytes])
29        } else {
30            new_boxed(header, &[bytes, &[0]])
31        }
32    }
33
34    /// Returns the underlying [`TagType`].
35    #[must_use]
36    pub fn typ(&self) -> TagType {
37        self.header.typ.into()
38    }
39
40    /// Returns the underlying tag size.
41    #[must_use]
42    pub const fn size(&self) -> usize {
43        self.header.size as usize
44    }
45
46    /// Reads the name of the bootloader that is booting the kernel as Rust
47    /// string slice without the null-byte.
48    ///
49    /// For example, this returns `"GRUB 2.02~beta3-5"`.
50    ///
51    /// If the function returns `Err` then perhaps the memory is invalid.
52    ///
53    /// # Examples
54    ///
55    /// ```rust,no_run
56    /// # use multiboot2::{BootInformation, BootInformationHeader};
57    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
58    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
59    /// if let Some(tag) = boot_info.boot_loader_name_tag() {
60    ///     assert_eq!(Ok("GRUB 2.02~beta3-5"), tag.name());
61    /// }
62    /// ```
63    pub fn name(&self) -> Result<&str, StringError> {
64        parse_slice_as_string(&self.name)
65    }
66}
67
68impl Debug for BootLoaderNameTag {
69    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
70        f.debug_struct("BootLoaderNameTag")
71            .field("typ", &self.header.typ)
72            .field("size", &self.header.size)
73            .field("name", &self.name())
74            .finish()
75    }
76}
77
78impl MaybeDynSized for BootLoaderNameTag {
79    type Header = TagHeader;
80
81    const BASE_SIZE: usize = mem::size_of::<TagHeader>();
82
83    fn dst_len(header: &TagHeader) -> usize {
84        assert!(header.size as usize >= Self::BASE_SIZE);
85        header.size as usize - Self::BASE_SIZE
86    }
87}
88
89impl Tag for BootLoaderNameTag {
90    type IDType = TagType;
91
92    const ID: TagType = TagType::BootLoaderName;
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::GenericInfoTag;
99    use core::borrow::Borrow;
100    use multiboot2_common::test_utils::AlignedBytes;
101
102    #[rustfmt::skip]
103    fn get_bytes() -> AlignedBytes<16> {
104        AlignedBytes::new([
105            TagType::BootLoaderName.val() as u8, 0, 0, 0,
106            14, 0, 0, 0,
107            b'h', b'e', b'l', b'l', b'o', b'\0',
108            /* padding */
109            0, 0
110        ])
111    }
112
113    /// Tests to parse a string with a terminating null byte from the tag (as the spec defines).
114    #[test]
115    fn test_parse_str() {
116        let bytes = get_bytes();
117        let tag = GenericInfoTag::ref_from_slice(bytes.borrow()).unwrap();
118        let tag = tag.cast::<BootLoaderNameTag>();
119        assert_eq!(tag.header.typ, TagType::BootLoaderName);
120        assert_eq!(tag.name(), Ok("hello"));
121    }
122
123    /// Test to generate a tag from a given string.
124    #[test]
125    #[cfg(feature = "builder")]
126    fn test_build_str() {
127        let tag = BootLoaderNameTag::new("hello");
128        let bytes = tag.as_bytes().as_ref();
129        let bytes = &bytes[..tag.header.size as usize];
130        assert_eq!(bytes, &get_bytes()[..tag.header.size as usize]);
131        assert_eq!(tag.name(), Ok("hello"));
132
133        // With terminating null.
134        let tag = BootLoaderNameTag::new("hello\0");
135        let bytes = tag.as_bytes().as_ref();
136        let bytes = &bytes[..tag.header.size as usize];
137        assert_eq!(bytes, &get_bytes()[..tag.header.size as usize]);
138        assert_eq!(tag.name(), Ok("hello"));
139
140        // test also some bigger message
141        let tag = BootLoaderNameTag::new("AbCdEfGhUjK YEAH");
142        assert_eq!(tag.name(), Ok("AbCdEfGhUjK YEAH"));
143        let tag = BootLoaderNameTag::new("AbCdEfGhUjK YEAH".repeat(42).as_str());
144        assert_eq!(tag.name(), Ok("AbCdEfGhUjK YEAH".repeat(42).as_str()));
145    }
146}