td_shim_interface/td_uefi_pi/pi/
fv.rs

1// Copyright © 2019 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! UEFI-PI storage service.
16//!
17//! The UEFI storage service is composed of Firmware Volume, Firmware Filesystem, File and Section.
18//!
19//! A Firmware Volume (FV) is a logical firmware device. In this specification, the basic storage
20//! repository for data and/or code is the firmware volume. Each firmware volume is organized into
21//! a  file system. As such, the file is the base unit of storage for firmware.
22//!
23//! A firmware file system (FFS) describes the organization of files and (optionally) free space
24//! within the firmware volume. Each firmware file system has a unique GUID, which is used by the
25//! firmware to associate a driver with a newly exposed firmware volume.
26//!
27//! Firmware files are code and/or data stored in firmware volumes. A firmware file may contain
28//! multiple sections.
29//!
30//! Firmware file sections are separate discrete “parts” within certain file types.
31use core::mem::size_of;
32use core::ptr::slice_from_raw_parts;
33use r_efi::efi::Guid;
34use scroll::{Pread, Pwrite};
35
36pub type FvbAttributes2 = u32;
37pub const FVH_REVISION: u8 = 2;
38
39// Calculate the 8-bit sum of all elements in a u8 slice
40fn sum8(data: &[u8]) -> u8 {
41    let mut sum = 0u8;
42    let cnt = data.len();
43    for item in data.iter().take(cnt) {
44        sum = sum.wrapping_add(*item);
45    }
46    sum
47}
48
49// Calculate the 16-bit sum of all elements in a u8 slice
50fn sum16(data: &[u8]) -> u16 {
51    let mut sum = 0u16;
52    let cnt = data.len() / 2;
53    for i in 0..cnt {
54        sum = sum.wrapping_add((data[i * 2 + 1] as u16) << 8 | data[i * 2] as u16);
55    }
56    if cnt * 2 == data.len() - 1 {
57        sum = sum.wrapping_add(data[cnt * 2] as u16)
58    }
59    sum
60}
61
62/// Firmware volume signature defined in [UEFI-PI] section 3.2.1
63pub const FVH_SIGNATURE: u32 = 0x4856465F; // '_','F','V','H'
64
65/// Firmware volume header defined in [UEFI-PI] section "3.2.1 Firmware Volume".
66///
67/// A firmware volume based on a block device begins with a header that describes the features and
68/// layout of the firmware volume. This header includes a description of the capabilities, state,
69/// and block map of the device.
70#[repr(C)]
71#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
72pub struct FirmwareVolumeHeader {
73    pub zero_vector: [u8; 16],
74    pub file_system_guid: [u8; 16], // Guid
75    pub fv_length: u64,
76    pub signature: u32,
77    pub attributes: FvbAttributes2,
78    pub header_length: u16,
79    pub checksum: u16,
80    pub ext_header_offset: u16,
81    pub reserved: u8,
82    pub revision: u8,
83}
84
85impl FirmwareVolumeHeader {
86    pub fn as_bytes(&self) -> &[u8] {
87        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
88    }
89
90    // Calculate and update the checksum of the FirmwareVolumeHeader
91    pub fn update_checksum(&mut self) {
92        // Clear the existing one before we calculate the chesum
93        self.checksum = 0;
94        self.checksum = (u16::MAX - sum16(self.as_bytes())).wrapping_add(1);
95    }
96
97    // Validate the checksum of the FirmwareVolumeHeader
98    pub fn validate_checksum(&self) -> bool {
99        sum16(self.as_bytes()) == 0
100    }
101}
102
103/// Firmware block map.
104///
105/// The block map is a run-length-encoded array of logical block definitions. This design allows a
106/// reasonable mechanism of describing the block layout of typical firmware devices. Each block can
107/// be referenced by its logical block address (LBA). The LBA is a zero-based enumeration of all of
108/// the blocks—i.e., LBA 0 is the first block, LBA 1 is the second block, and LBA n is the (n-1)
109/// device. The header is always located at the beginning of LBA 0.
110#[repr(C)]
111#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
112pub struct FvBlockMap {
113    pub num_blocks: u32,
114    pub length: u32,
115}
116
117impl FvBlockMap {
118    pub fn as_bytes(&self) -> &[u8] {
119        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
120    }
121}
122
123/// Firmware Volume Extended Header pointed to by `FirmwareVolumeHeader::ext_header_offset`.
124///
125/// The extended header is followed by zero or more variable length extension entries.
126/// Each extension entry is prefixed with the EFI_FIRMWARE_VOLUME_EXT_ENTRY structure, which
127/// defines the type and size of the extension entry. The extended header is always 32-bit aligned
128/// relative to the start of the FIRMWARE VOLUME.
129#[repr(C)]
130#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
131pub struct FirmwareVolumeExtHeader {
132    pub fv_name: [u8; 16], // Guid
133    pub ext_header_size: u32,
134}
135
136impl FirmwareVolumeExtHeader {
137    pub fn as_bytes(&self) -> &[u8] {
138        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
139    }
140}
141
142/// Firmware volume extension entry.
143///
144/// After the extension header, there is an array of variable-length extension header entries,
145/// each prefixed with the EFI_FIRMWARE_VOLUME_EXT_ENTRY structure.
146#[repr(C)]
147#[derive(Copy, Clone, Debug)]
148pub struct FirmwareVolumeExtEntry {
149    pub ext_entry_size: u16,
150    pub ext_entry_type: u32,
151}
152
153impl FirmwareVolumeExtEntry {
154    pub fn as_bytes(&self) -> &[u8] {
155        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
156    }
157}
158
159/// EFI_FIRMWARE_FILE_SYSTEM2_GUID defined in [UEFI-PI Spec], section 3.2.2
160pub const FIRMWARE_FILE_SYSTEM2_GUID: r_efi::base::Guid = r_efi::base::Guid::from_fields(
161    0x8c8ce578,
162    0x8a3d,
163    0x4f1c,
164    0x99,
165    0x35,
166    &[0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3],
167);
168
169/// EFI_FIRMWARE_FILE_SYSTEM3_GUID defined in [UEFI-PI Spec], section 3.2.2
170pub const FIRMWARE_FILE_SYSTEM3_GUID: r_efi::base::Guid = r_efi::base::Guid::from_fields(
171    0x5473c07a,
172    0x3dcb,
173    0x4dca,
174    0xbd,
175    0x6f,
176    &[0x1e, 0x96, 0x89, 0xe7, 0x34, 0x9a],
177);
178
179/// Firmware File Types defined in [UEFI-PI], section 2.1.4.1
180pub type FvFileType = u8;
181
182pub const FV_FILETYPE_RAW: u8 = 0x01;
183pub const FV_FILETYPE_FREEFORM: u8 = 0x02;
184pub const FV_FILETYPE_SECURITY_CORE: u8 = 0x03;
185pub const FV_FILETYPE_PEI_CORE: u8 = 0x04;
186pub const FV_FILETYPE_DXE_CORE: u8 = 0x05;
187pub const FV_FILETYPE_PEIM: u8 = 0x06;
188pub const FV_FILETYPE_DRIVER: u8 = 0x07;
189pub const FV_FILETYPE_COMBINED_PEIM_DRIVER: u8 = 0x08;
190pub const FV_FILETYPE_APPLICATION: u8 = 0x09;
191pub const FV_FILETYPE_MM: u8 = 0x0A;
192pub const FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: u8 = 0x0B;
193pub const FV_FILETYPE_COMBINED_MM_DXE: u8 = 0x0C;
194pub const FV_FILETYPE_MM_CORE: u8 = 0x0D;
195pub const FV_FILETYPE_MM_STANDALONE: u8 = 0x0E;
196pub const FV_FILETYPE_MM_CORE_STANDALONE: u8 = 0x0F;
197pub const FV_FILETYPE_FFS_PAD: u8 = 0xF0;
198
199//
200// FFS File Attributes.
201//
202pub const FFS_ATTRIB_LARGE_FILE: u8 = 0x01;
203pub const FFS_ATTRIB_DATA_ALIGNMENT2: u8 = 0x02;
204pub const FFS_ATTRIB_FIXED: u8 = 0x04;
205pub const FFS_ATTRIB_DATA_ALIGNMENT: u8 = 0x38;
206pub const FFS_ATTRIB_CHECKSUM: u8 = 0x40;
207//
208// FFS_FIXED_CHECKSUM is the checksum value used when the
209// FFS_ATTRIB_CHECKSUM attribute bit is clear
210//
211pub const FFS_FIXED_CHECKSUM: u8 = 0xAA;
212
213// FFS File State Bits
214pub const EFI_FILE_HEADER_CONSTRUCTION: u8 = 0x01;
215pub const EFI_FILE_HEADER_VALID: u8 = 0x02;
216pub const EFI_FILE_DATA_VALID: u8 = 0x04;
217pub const EFI_FILE_MARKED_FOR_UPDATE: u8 = 0x08;
218pub const EFI_FILE_DELETED: u8 = 0x10;
219pub const EFI_FILE_HEADER_INVALID: u8 = 0x20;
220
221pub type FfsFileAttributes = u8;
222pub type FfsFileState = u8;
223
224#[repr(C)]
225#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
226pub struct Checksum {
227    pub header: u8,
228    pub file: u8,
229}
230
231/// File Header for files smaller than 16Mb, define in [UEFI-PI Spec] section 2.2.3
232///
233/// All FFS files begin with a header that is aligned on an 8-byteboundry with respect to the
234/// beginning of the firmware volume. FFS files can contain the following parts: Header and Data.
235/// It is possible to create a file that has only a header and no data, which consumes 24 bytes
236/// of space. This type of file is known as a zero-length file. If the file contains data,
237/// the data immediately follows the header. The format of the data within a file is defined by the
238/// Type field in the header, either EFI_FFS_FILE_HEADER or EFI_FFS_FILE_HEADER2.
239/// If the file length is bigger than 16MB, EFI_FFS_FILE_HEADER2 must be used.
240#[repr(C)]
241#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
242pub struct FfsFileHeader {
243    pub name: [u8; 16], // Guid,
244    pub integrity_check: Checksum,
245    pub r#type: FvFileType,
246    pub attributes: FfsFileAttributes,
247    pub size: [u8; 3],
248    pub state: FfsFileState,
249}
250
251impl FfsFileHeader {
252    pub fn as_bytes(&self) -> &[u8] {
253        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
254    }
255
256    // Calculate and update the checksum of the FfsFileHeader
257    pub fn update_checksum(&mut self) {
258        // Clear the existing one before we calculate the checksum
259        self.integrity_check.header = 0;
260        self.integrity_check.file = 0;
261        self.state = 0;
262        self.integrity_check.header = (u8::MAX - sum8(self.as_bytes())).wrapping_add(1);
263
264        self.integrity_check.file = FFS_FIXED_CHECKSUM;
265        self.state = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
266    }
267
268    // Validate the checksum of the FfsFileHeader
269    pub fn validate_checksum(&self) -> bool {
270        let sum = sum8(self.as_bytes());
271        sum ^ ((EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID)
272            + FFS_FIXED_CHECKSUM)
273            == 0
274    }
275}
276
277/// File Header 2 for files larger than 16Mb, define in [UEFI-PI Spec] section 2.2.3
278///
279/// All FFS files begin with a header that is aligned on an 8-byteboundry with respect to the
280/// beginning of the firmware volume. FFS files can contain the following parts: Header and Data.
281/// It is possible to create a file that has only a header and no data, which consumes 24 bytes
282/// of space. This type of file is known as a zero-length file. If the file contains data,
283/// the data immediately follows the header. The format of the data within a file is defined by the
284/// Type field in the header, either EFI_FFS_FILE_HEADER or EFI_FFS_FILE_HEADER2.
285/// If the file length is bigger than 16MB, EFI_FFS_FILE_HEADER2 must be used.
286#[repr(C)]
287#[derive(Copy, Clone, Debug)]
288pub struct FfsFileHeader2 {
289    pub name: Guid,
290    pub integrity_check: u16,
291    pub r#type: FvFileType,
292    pub attributes: FfsFileAttributes,
293    pub size: [u8; 3],
294    pub state: FfsFileState,
295    pub extended_size: u32,
296}
297
298impl FfsFileHeader2 {
299    pub fn as_bytes(&self) -> &[u8] {
300        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
301    }
302}
303
304/// Firmware File Section Types defined in [UEFI-PI], section 2.1.5.1
305pub type SectionType = u8;
306
307pub const SECTION_ALL: u8 = 0x00;
308
309pub const SECTION_COMPRESSION: u8 = 0x01;
310pub const SECTION_GUID_DEFINED: u8 = 0x02;
311pub const SECTION_DISPOSABLE: u8 = 0x03;
312
313pub const SECTION_PE32: u8 = 0x10;
314pub const SECTION_PIC: u8 = 0x11;
315pub const SECTION_TE: u8 = 0x12;
316pub const SECTION_DXE_DEPEX: u8 = 0x13;
317pub const SECTION_VERSION: u8 = 0x14;
318pub const SECTION_USER_INTERFACE: u8 = 0x15;
319pub const SECTION_COMPATIBILITY16: u8 = 0x16;
320pub const SECTION_FIRMWARE_VOLUME_IMAGE: u8 = 0x17;
321pub const SECTION_FREEFORM_SUBTYPE_GUID: u8 = 0x18;
322pub const SECTION_RAW: u8 = 0x19;
323pub const SECTION_PEI_DEPEX: u8 = 0x1B;
324pub const SECTION_MM_DEPEX: u8 = 0x1C;
325
326/// Section Header for files smaller than 16Mb, define in [UEFI-PI Spec] section 2.2.4
327#[repr(C)]
328#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
329pub struct CommonSectionHeader {
330    pub size: [u8; 3],
331    pub r#type: SectionType,
332}
333
334impl CommonSectionHeader {
335    pub fn as_bytes(&self) -> &[u8] {
336        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
337    }
338}
339
340/// Section Header 2 for files larger than 16Mb, define in [UEFI-PI Spec] section 2.2.4
341#[repr(C)]
342#[derive(Copy, Clone, Debug)]
343pub struct CommonSectionHeader2 {
344    pub size: [u8; 3],
345    pub r#type: SectionType,
346    pub extended_size: u32,
347}
348
349impl CommonSectionHeader2 {
350    pub fn as_bytes(&self) -> &[u8] {
351        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    #[test]
360    fn test_fvh_checksum() {
361        let mut header = FirmwareVolumeHeader::default();
362        header.attributes = 0x4feff;
363        header.revision = FVH_REVISION;
364        header.signature = FVH_SIGNATURE;
365        header.header_length = size_of::<FirmwareVolumeHeader>() as u16;
366        header.fv_length = 0x1000;
367        header.checksum = 0x3fef;
368        header.update_checksum();
369
370        assert_eq!(header.checksum, 0x6010);
371        assert!(header.validate_checksum());
372
373        header.checksum = 0x3fe6;
374        assert!(!header.validate_checksum());
375
376        header.update_checksum();
377        assert_eq!(header.checksum, 0x6010);
378        assert!(header.validate_checksum());
379    }
380
381    #[test]
382    fn test_ffsh_checksum() {
383        let mut header = FfsFileHeader::default();
384        header.r#type = FV_FILETYPE_FFS_PAD;
385
386        header.update_checksum();
387        assert_eq!(header.integrity_check.header, 0x10);
388        assert!(header.validate_checksum());
389
390        header.name = *Guid::from_fields(
391            0x00000001,
392            0x0000,
393            0x0000,
394            0x00,
395            0x00,
396            &[0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
397        )
398        .as_bytes();
399        assert!(!header.validate_checksum());
400        header.update_checksum();
401        assert_eq!(header.integrity_check.header, 0xC);
402        assert!(header.validate_checksum());
403    }
404
405    #[test]
406    fn test_update_checksum() {
407        let mut header = FfsFileHeader::default();
408        header.update_checksum();
409
410        let mut header = FirmwareVolumeHeader::default();
411        header.update_checksum();
412    }
413}