Skip to main content

intelflash/
lib.rs

1// SPDX-License-Identifier: MIT
2
3#![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                /*
200                self.i += mem::size_of::<volume::Header>();
201
202                while self.i + mem::size_of::<volume::BlockEntry>() <= self.data.len() {
203                    let block_entry = plain::from_bytes::<volume::BlockEntry>(&self.data[self.i..]).unwrap();
204                    self.i += mem::size_of::<volume::BlockEntry>();
205
206                    if block_entry.num_blocks == 0 && block_entry.block_length == 0 {
207                        break;
208                    } else {
209                        println!("    {}, {}", block_entry.num_blocks, block_entry.block_length);
210                    }
211                }
212
213                self.i += header.header_length as usize - mem::size_of::<volume::Header>();
214                */
215
216                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}