Skip to main content

multiboot2_header/
console.rs

1use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
2use multiboot2_common::{MaybeDynSized, Tag};
3
4multiboot2_common::raw_type! {
5    /// ABI compatible representation of the console flags of the
6    /// [`ConsoleHeaderTag`].
7    ///
8    /// This type matches the binary representation (`u32`).
9    pub struct ConsoleHeaderTagFlagsRaw(u32);
10
11    /// The console flags of the [`ConsoleHeaderTag`].
12    ///
13    /// This is a higher level abstraction for [`ConsoleHeaderTagFlagsRaw`].
14    pub enum ConsoleHeaderTagFlags {
15        /// At least one of the consoles supported by the bootloader must be
16        /// present and information about it must be available in the boot
17        /// information.
18        ConsoleRequired = 1,
19        /// The OS image has EGA text support.
20        EgaTextSupported = 2,
21    }
22}
23
24/// Tells that a console must be available in MBI.
25/// Only relevant for legacy BIOS.
26#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[repr(C, align(8))]
28pub struct ConsoleHeaderTag {
29    header: HeaderTagHeader,
30    console_flags: ConsoleHeaderTagFlagsRaw,
31}
32
33impl ConsoleHeaderTag {
34    /// Constructs a new tag.
35    #[must_use]
36    pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self {
37        let header =
38            HeaderTagHeader::new(HeaderTagType::ConsoleFlags, flags, Self::BASE_SIZE as u32);
39        Self {
40            header,
41            console_flags: ConsoleHeaderTagFlagsRaw::new(console_flags.val()),
42        }
43    }
44
45    /// Returns the [`HeaderTagType`].
46    #[must_use]
47    pub const fn typ(&self) -> HeaderTagType {
48        self.header.typ()
49    }
50
51    /// Returns the [`HeaderTagFlag`]s.
52    #[must_use]
53    pub const fn flags(&self) -> HeaderTagFlag {
54        self.header.flags()
55    }
56
57    /// Returns the size.
58    #[must_use]
59    pub const fn size(&self) -> u32 {
60        self.header.size()
61    }
62
63    /// Returns the [`ConsoleHeaderTagFlags`].
64    #[must_use]
65    pub const fn console_flags(&self) -> ConsoleHeaderTagFlags {
66        ConsoleHeaderTagFlags::from_val(self.console_flags.get())
67    }
68}
69
70// SAFETY: The tag is repr(C) with the header as first field, any bit
71// pattern is valid, and `BASE_SIZE`/`dst_len` match the ABI.
72unsafe impl MaybeDynSized for ConsoleHeaderTag {
73    type Header = HeaderTagHeader;
74
75    const BASE_SIZE: usize = size_of::<HeaderTagHeader>() + size_of::<u32>();
76}
77
78impl Tag for ConsoleHeaderTag {
79    type IDType = HeaderTagType;
80    const ID: HeaderTagType = HeaderTagType::ConsoleFlags;
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::GenericHeaderTag;
87    use core::borrow::Borrow;
88    use multiboot2_common::test_utils::AlignedBytes;
89
90    /// The console flag values must match the values mandated by the
91    /// specification.
92    #[test]
93    fn console_flags_match_spec_values() {
94        assert_eq!(ConsoleHeaderTagFlags::ConsoleRequired.val(), 1);
95        assert_eq!(ConsoleHeaderTagFlags::EgaTextSupported.val(), 2);
96    }
97
98    /// A tag with a console flags value unknown to the specification must be
99    /// parsable without undefined behavior.
100    #[test]
101    fn unknown_console_flags_are_not_ub() {
102        #[rustfmt::skip]
103        let bytes = AlignedBytes::new([
104            /* typ = console flags */
105            4, 0,
106            /* flags */
107            0, 0,
108            /* size */
109            12, 0, 0, 0,
110            /* console_flags = 3 (unknown) */
111            3, 0, 0, 0,
112            /* padding to alignment */
113            0, 0, 0, 0,
114        ]);
115        let tag = GenericHeaderTag::ref_from_slice(bytes.borrow())
116            .unwrap()
117            .cast::<ConsoleHeaderTag>();
118
119        assert_eq!(tag.console_flags(), ConsoleHeaderTagFlags::Custom(3));
120    }
121}