1#![no_std]
4
5#[macro_use]
6extern crate alloc;
7
8use alloc::string::String;
9use core::{fmt, mem};
10
11#[derive(Copy, Clone, Debug)]
12#[repr(usize)]
13pub enum RegionKind {
14 Descriptor = 0,
15 Bios = 1,
16 ManagementEngine = 2,
17 Ethernet = 3,
18 PlatformData = 4,
19 Reserved5 = 5,
20 Reserved6 = 6,
21 Reserved7 = 7,
22 EmbeddedController = 8,
23}
24
25impl fmt::Display for RegionKind {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 let name = match self {
28 RegionKind::Descriptor => "Flash Descriptor",
29 RegionKind::Bios => "BIOS",
30 RegionKind::ManagementEngine => "Intel ME",
31 RegionKind::Ethernet => "GbE",
32 RegionKind::PlatformData => "Platform Data",
33 RegionKind::EmbeddedController => "EC",
34 _ => "Reserved",
35 };
36 write!(f, "{}", name)
37 }
38}
39
40pub const HAP: u32 = 0x10000;
41
42pub mod file;
43pub mod flash;
44pub mod section;
45pub mod volume;
46
47pub struct Rom<'a> {
48 data: &'a [u8],
49 descriptor: &'a flash::Descriptor,
50}
51
52impl<'a> Rom<'a> {
53 pub fn new(data: &'a [u8]) -> Result<Rom, String> {
54 let mut i = 16;
55
56 while i + mem::size_of::<flash::Descriptor>() <= data.len() {
57 if data[i..i + 4] == [0x5a, 0xa5, 0xf0, 0x0f] {
58 return Ok(Rom {
59 data: &data[i - 16..],
60 descriptor: plain::from_bytes(&data[i..]).map_err(|err| {
61 format!("Flash descriptor invalid: {:?}", err)
62 })?
63 });
64 }
65
66 i += 4;
67 }
68
69 Err(format!("Flash descriptor not found"))
70 }
71
72 pub fn data(&self) -> &'a [u8] {
73 self.data
74 }
75
76 pub fn flash_descriptor(&self) -> &'a flash::Descriptor {
77 self.descriptor
78 }
79
80 pub fn flash_region(&self) -> Result<&'a flash::Region, String> {
81 let offset = (((self.descriptor.map0 >> 16) & 0xff) << 4) as usize;
82
83 if offset >= self.data.len() {
84 return Err(format!("Flash region table truncated"))
85 }
86
87 plain::from_bytes(&self.data[offset..]).map_err(|err| {
88 format!("Flash region table invalid: {:?}", err)
89 })
90 }
91
92 pub fn flash_pchstrap(&self) -> Result<&'a flash::PchStrap, String> {
93 let offset = (((self.descriptor.map1 >> 16) & 0xff) << 4) as usize;
94
95 if offset >= self.data.len() {
96 return Err(format!("PCHSTRAP table truncated"))
97 }
98
99 plain::from_bytes(&self.data[offset..]).map_err(|err| {
100 format!("PCHSTRAP table invalid: {:?}", err)
101 })
102 }
103
104 pub fn high_assurance_platform(&self) -> Result<bool, String> {
105 let pchstrap = self.flash_pchstrap()?;
106 Ok(pchstrap.data[0] & HAP == HAP)
107 }
108
109 pub fn get_region_base_limit(&self, kind: RegionKind) -> Result<Option<(usize, usize)>, String> {
110 let frba = self.flash_region()?;
111
112 let reg = frba.data[kind as usize];
113
114 let base_mask = 0x7fff;
115 let limit_mask = base_mask << 16;
116
117 let base = (reg & base_mask) << 12;
118 let limit = ((reg & limit_mask) >> 4) | 0xfff;
119
120 if limit > base {
121 Ok(Some((base as usize, limit as usize)))
122 } else {
123 Ok(None)
124 }
125 }
126
127 pub fn get_region(&self, kind: RegionKind) -> Result<Option<&'a [u8]>, String> {
128 if let Some((base, limit)) = self.get_region_base_limit(kind)? {
129 if (limit as usize) < self.data.len() {
130 Ok(Some(&self.data[base as usize..limit as usize + 1]))
131 } else {
132 Err(format!("{:?} region invalid: {} >= {}", kind, limit, self.data.len()))
133 }
134 } else {
135 Ok(None)
136 }
137 }
138
139 pub fn bios(&self) -> Result<Option<Bios<'a>>, String> {
140 if let Some(data) = self.get_region(RegionKind::Bios)? {
141 Ok(Some(Bios { data }))
142 } else {
143 Ok(None)
144 }
145 }
146
147 pub fn me(&self) -> Result<Option<Me<'a>>, String> {
148 if let Some(data) = self.get_region(RegionKind::ManagementEngine)? {
149 Ok(Some(Me { data }))
150 } else {
151 Ok(None)
152 }
153 }
154}
155
156pub struct Bios<'a> {
157 data: &'a [u8],
158}
159
160impl<'a> Bios<'a> {
161 pub fn new(data: &'a [u8]) -> Result<Bios, String> {
162 Ok(Bios { data })
163 }
164
165 pub fn data(&self) -> &'a [u8] {
166 self.data
167 }
168
169 pub fn volumes(&self) -> BiosVolumes {
170 BiosVolumes::new(self.data)
171 }
172}
173
174pub struct BiosVolumes<'a> {
175 data: &'a [u8],
176 i: usize,
177}
178
179impl<'a> BiosVolumes<'a> {
180 pub fn new(data: &'a [u8]) -> Self {
181 Self {
182 data,
183 i: 0
184 }
185 }
186}
187
188impl<'a> Iterator for BiosVolumes<'a> {
189 type Item = BiosVolume<'a>;
190
191 fn next(&mut self) -> Option<Self::Item> {
192 while self.i + mem::size_of::<volume::Header>() <= self.data.len() {
193 let header_data = &self.data[self.i..];
194 let header = plain::from_bytes::<volume::Header>(header_data).unwrap();
195
196 if header.valid() {
197 self.i += header.length as usize;
198
199 return Some(BiosVolume {
217 header,
218 data: &header_data[header.header_length as usize .. header.length as usize]
219 });
220 } else {
221 self.i += 8;
222 }
223 }
224
225 None
226 }
227}
228
229pub struct BiosVolume<'a> {
230 header: &'a volume::Header,
231 data: &'a [u8],
232}
233
234impl<'a> BiosVolume<'a> {
235 pub fn header(&self) -> &'a volume::Header {
236 self.header
237 }
238
239 pub fn data(&self) -> &'a [u8] {
240 self.data
241 }
242
243 pub fn files(&self) -> BiosFiles {
244 BiosFiles::new(self.data)
245 }
246}
247
248pub struct BiosFiles<'a> {
249 data: &'a [u8],
250 i: usize,
251}
252
253impl<'a> BiosFiles<'a> {
254 pub fn new(data: &'a [u8]) -> Self {
255 Self {
256 data,
257 i: 0
258 }
259 }
260}
261
262impl<'a> Iterator for BiosFiles<'a> {
263 type Item = BiosFile<'a>;
264
265 fn next(&mut self) -> Option<Self::Item> {
266 if self.i + mem::size_of::<file::Header>() <= self.data.len() {
267 let header_data = &self.data[self.i..];
268 let header = plain::from_bytes::<file::Header>(header_data).unwrap();
269
270 if header.size() == 0xFFFFFF {
271 self.i = self.data.len();
272 None
273 } else {
274 self.i += ((header.size() + 7) / 8) * 8;
275
276 Some(BiosFile {
277 header,
278 data: &header_data[mem::size_of::<file::Header>() .. header.size()]
279 })
280 }
281 } else {
282 None
283 }
284 }
285}
286
287pub struct BiosFile<'a> {
288 header: &'a file::Header,
289 data: &'a [u8],
290}
291
292impl<'a> BiosFile<'a> {
293 pub fn header(&self) -> &'a file::Header {
294 self.header
295 }
296
297 pub fn data(&self) -> &'a [u8] {
298 self.data
299 }
300
301 pub fn sections(&self) -> BiosSections {
302 BiosSections::new(self.data)
303 }
304}
305
306pub struct BiosSections<'a> {
307 data: &'a [u8],
308 i: usize,
309}
310
311impl<'a> BiosSections<'a> {
312 pub fn new(data: &'a [u8]) -> Self {
313 Self {
314 data,
315 i: 0
316 }
317 }
318}
319
320impl<'a> Iterator for BiosSections<'a> {
321 type Item = BiosSection<'a>;
322
323 fn next(&mut self) -> Option<Self::Item> {
324 if self.i + mem::size_of::<section::Header>() <= self.data.len() {
325 let header_data = &self.data[self.i..];
326 let header = plain::from_bytes::<section::Header>(header_data).unwrap();
327
328 if header.size() == 0xFFFFFF {
329 self.i = self.data.len();
330 None
331 } else {
332
333 self.i += ((header.size() + 3) / 4) * 4;
334
335 Some(BiosSection {
336 header,
337 data: &header_data[mem::size_of::<section::Header>() .. header.size()]
338 })
339 }
340 } else {
341 None
342 }
343 }
344}
345
346pub struct BiosSection<'a> {
347 header: &'a section::Header,
348 data: &'a [u8],
349}
350
351impl<'a> BiosSection<'a> {
352 pub fn header(&self) -> &'a section::Header {
353 self.header
354 }
355
356 pub fn data(&self) -> &'a [u8] {
357 self.data
358 }
359}
360
361pub struct Me<'a> {
362 data: &'a [u8],
363}
364
365impl<'a> Me<'a> {
366 pub fn new(data: &'a [u8]) -> Result<Me, String> {
367 Ok(Me { data })
368 }
369
370 pub fn data(&self) -> &'a [u8] {
371 self.data
372 }
373
374 pub fn version(&self) -> Option<String> {
375 let mut i = 0;
376 while i + 4 <= self.data.len() {
377 if &self.data[i..i + 4] == b"$FPT" {
378 break;
379 }
380 i += 1;
381 }
382
383 if i + 0x20 <= self.data.len() {
384 let mut version = String::new();
385
386 let bytes = &self.data[i + 0x18..i + 0x20];
387 for part in bytes.chunks(2) {
388 if ! version.is_empty() {
389 version.push('.');
390 }
391 version.push_str(&format!("{}", part[0] as u16 | (part[1] as u16) << 8));
392 }
393
394 Some(version)
395 } else {
396 None
397 }
398 }
399
400 pub fn modules(&self) -> Option<u32> {
401 if self.data.len() >= 0x18 {
402 let bytes = &self.data[0x14..0x18];
403 Some(bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24)
404 } else {
405 None
406 }
407 }
408}