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
use crate::TagType;
use core::str::Utf8Error;

/// This tag contains the name of the bootloader that is booting the kernel.
///
/// The name is a normal C-style UTF-8 zero-terminated string that can be
/// obtained via the `name` method.
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
pub struct BootLoaderNameTag {
    typ: TagType,
    size: u32,
    /// Null-terminated UTF-8 string
    string: u8,
}

impl BootLoaderNameTag {
    /// Read the name of the bootloader that is booting the kernel.
    /// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
    /// is invalid or the bootloader doesn't follow the spec.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// if let Some(tag) = boot_info.boot_loader_name_tag() {
    ///     let name = tag.name();
    ///     assert_eq!("GRUB 2.02~beta3-5", name);
    /// }
    /// ```
    pub fn name(&self) -> Result<&str, Utf8Error> {
        use core::{mem, slice, str};
        // strlen without null byte
        let strlen = self.size as usize - mem::size_of::<BootLoaderNameTag>();
        let bytes = unsafe { slice::from_raw_parts((&self.string) as *const u8, strlen) };
        str::from_utf8(bytes)
    }
}

#[cfg(test)]
mod tests {
    use crate::TagType;

    const MSG: &str = "hello";

    /// Returns the tag structure in bytes in native endian format.
    fn get_bytes() -> std::vec::Vec<u8> {
        // 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()),
            &size.to_ne_bytes(),
            MSG.as_bytes(),
            // Null Byte
            &[0],
        ]
        .iter()
        .flat_map(|bytes| bytes.iter())
        .copied()
        .collect()
    }

    /// Tests to parse a string with a terminating null byte from the tag (as the spec defines).
    #[test]
    fn test_parse_str() {
        let tag = get_bytes();
        let tag = unsafe {
            tag.as_ptr()
                .cast::<super::BootLoaderNameTag>()
                .as_ref()
                .unwrap()
        };
        assert_eq!({ tag.typ }, TagType::BootLoaderName);
        assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
    }
}