multiboot2_host/header/
tag.rs

1use std::convert::TryFrom;
2
3use byteorder::{ReadBytesExt, LE};
4use num_enum::TryFromPrimitive;
5use thiserror::Error;
6
7#[repr(u16)]
8#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
9pub enum TagType {
10    End = 0,
11    InfoRequest = 1,
12    LoadAddr = 2,
13    EntryAddr = 3,
14    ConsoleFlags = 4,
15    Framebuffer = 5,
16    ModuleAlign = 6,
17    EfiBootServices = 7,
18    EntryAddrEfi32 = 8,
19    EntryAddrEfi64 = 9,
20    Relocatable = 10,
21
22    #[cfg(feature = "hvm")]
23    HybridRuntime = 0xF00D,
24}
25
26impl TagType {
27    fn read_fields<R: std::io::Read>(&self, size: u32, mut r: R) -> std::io::Result<Tag> {
28        let mut get_u32 = || r.read_u32::<LE>();
29
30        let tag = match self {
31            Self::End => Tag::End,
32            Self::InfoRequest => {
33                let num_requests = (size as usize - 8) / 4;
34                let mut mbi_tag_types = vec![0u32; num_requests];
35                r.read_u32_into::<LE>(&mut mbi_tag_types)?;
36                Tag::InfoRequest { mbi_tag_types }
37            }
38            Self::LoadAddr => Tag::LoadAddr {
39                header_addr: get_u32()?,
40                load_addr: get_u32()?,
41                load_end_addr: get_u32()?,
42                bss_end_addr: get_u32()?,
43            },
44            Self::EntryAddr => Tag::EntryAddr(get_u32()?),
45            Self::EntryAddrEfi32 => Tag::EntryAddrEfi32(get_u32()?),
46            Self::EntryAddrEfi64 => Tag::EntryAddrEfi64(get_u32()?),
47            Self::ConsoleFlags => Tag::ConsoleFlags(get_u32()?),
48            Self::Framebuffer => Tag::Framebuffer {
49                width: get_u32()?,
50                height: get_u32()?,
51                depth: get_u32()?,
52            },
53            Self::ModuleAlign => Tag::ModuleAlign,
54            Self::EfiBootServices => Tag::EfiBootServices,
55            Self::Relocatable => Tag::Relocatable {
56                min_addr: get_u32()?,
57                max_addr: get_u32()?,
58                align: get_u32()?,
59                preference: get_u32()?,
60            },
61            #[cfg(feature = "hvm")]
62            Self::HybridRuntime => {
63                let mut fields = [0u64; 6];
64                r.read_u64_into::<LE>(&mut fields)?;
65                Tag::HybridRuntime {
66                    flags: fields[0],
67                    gpa_map_req: fields[1],
68                    hrt_hihalf_offset: fields[2],
69                    nautilus_entry_gva: fields[3],
70                    comm_page_gpa: fields[4],
71                    int_vec: fields[5],
72                }
73            }
74        };
75
76        match size.checked_sub(tag.size()) {
77            Some(0) => (),
78            Some(n @ 1..=7) => {
79                // Padding bytes; just read past them
80                let mut dummy = [0; 7];
81                r.read_exact(&mut dummy[0..n as usize])?;
82            }
83            _ => {
84                return Err(std::io::Error::new(
85                    std::io::ErrorKind::InvalidData,
86                    format!("Unexpected tag size: expected {}, got {}", tag.size(), size),
87                ))
88            }
89        }
90
91        Ok(tag)
92    }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum Tag {
97    End,
98    InfoRequest {
99        mbi_tag_types: Vec<u32>,
100    },
101    LoadAddr {
102        header_addr: u32,
103        load_addr: u32,
104        load_end_addr: u32,
105        bss_end_addr: u32,
106    },
107    EntryAddr(u32),
108    EntryAddrEfi32(u32),
109    EntryAddrEfi64(u32),
110    ConsoleFlags(u32),
111    Framebuffer {
112        width: u32,
113        height: u32,
114        depth: u32,
115    },
116    ModuleAlign,
117    EfiBootServices,
118    Relocatable {
119        min_addr: u32,
120        max_addr: u32,
121        align: u32,
122        preference: u32,
123    },
124    #[cfg(feature = "hvm")]
125    HybridRuntime {
126        flags: u64,
127        gpa_map_req: u64,
128        hrt_hihalf_offset: u64,
129        nautilus_entry_gva: u64,
130        comm_page_gpa: u64,
131        int_vec: u64,
132    },
133}
134
135#[derive(Error, Debug)]
136#[error("{self:?}")]
137pub struct UnknownTag {
138    tag_type: u16,
139    flags: u16,
140    size: u32,
141    data: Vec<u8>,
142}
143
144impl Tag {
145    pub fn tag_type(&self) -> TagType {
146        match self {
147            Self::End => TagType::End,
148            Self::InfoRequest { .. } => TagType::InfoRequest,
149            Self::LoadAddr { .. } => TagType::LoadAddr,
150            Self::EntryAddr { .. } => TagType::EntryAddr,
151            Self::EntryAddrEfi32(..) => TagType::EntryAddrEfi32,
152            Self::EntryAddrEfi64(..) => TagType::EntryAddrEfi64,
153            Self::ConsoleFlags(..) => TagType::ConsoleFlags,
154            Self::Framebuffer { .. } => TagType::Framebuffer,
155            Self::ModuleAlign => TagType::ModuleAlign,
156            Self::EfiBootServices => TagType::EfiBootServices,
157            Self::Relocatable { .. } => TagType::Relocatable,
158            #[cfg(feature = "hvm")]
159            Self::HybridRuntime { .. } => TagType::HybridRuntime,
160        }
161    }
162
163    pub fn size(&self) -> u32 {
164        match self {
165            Self::End | Self::ModuleAlign | Self::EfiBootServices => 8,
166            Self::EntryAddr(..)
167            | Self::EntryAddrEfi32(..)
168            | Self::EntryAddrEfi64(..)
169            | Self::ConsoleFlags(..) => 12,
170            Self::Framebuffer { .. } => 20,
171            Self::LoadAddr { .. } | Self::Relocatable { .. } => 24,
172            #[cfg(feature = "hvm")]
173            Self::HybridRuntime { .. } => 56,
174            Self::InfoRequest { mbi_tag_types } => 4 * mbi_tag_types.len() as u32 + 8,
175        }
176    }
177
178    pub fn from_reader<R: std::io::Read>(mut r: R) -> std::io::Result<Self> {
179        let ty = r.read_u16::<LE>()?;
180        let flags = r.read_u16::<LE>()?;
181        let size = r.read_u32::<LE>()?;
182
183        let tag = if let Ok(tag_type) = TagType::try_from(ty) {
184            tag_type.read_fields(size, &mut r)?
185        } else {
186            let mut data = vec![0u8; size as usize - 8];
187            r.read_exact(&mut data)?;
188            let unknown_tag = UnknownTag {
189                tag_type: ty,
190                flags,
191                size,
192                data,
193            };
194            return Err(std::io::Error::new(
195                std::io::ErrorKind::InvalidData,
196                unknown_tag,
197            ));
198        };
199
200        Ok(tag)
201    }
202}