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 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}