use core::mem::size_of;
use core::ptr::slice_from_raw_parts;
use r_efi::efi::Guid;
use scroll::{Pread, Pwrite};
pub type FvbAttributes2 = u32;
pub const FVH_REVISION: u8 = 2;
fn sum8(data: &[u8]) -> u8 {
let mut sum = 0u8;
let cnt = data.len();
for item in data.iter().take(cnt) {
sum = sum.wrapping_add(*item);
}
sum
}
fn sum16(data: &[u8]) -> u16 {
let mut sum = 0u16;
let cnt = data.len() / 2;
for i in 0..cnt {
sum = sum.wrapping_add((data[i * 2 + 1] as u16) << 8 | data[i * 2] as u16);
}
if cnt * 2 == data.len() - 1 {
sum = sum.wrapping_add(data[cnt * 2] as u16)
}
sum
}
pub const FVH_SIGNATURE: u32 = 0x4856465F;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct FirmwareVolumeHeader {
pub zero_vector: [u8; 16],
pub file_system_guid: [u8; 16], pub fv_length: u64,
pub signature: u32,
pub attributes: FvbAttributes2,
pub header_length: u16,
pub checksum: u16,
pub ext_header_offset: u16,
pub reserved: u8,
pub revision: u8,
}
impl FirmwareVolumeHeader {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
pub fn update_checksum(&mut self) {
self.checksum = 0;
self.checksum = (u16::MAX - sum16(self.as_bytes())).wrapping_add(1);
}
pub fn validate_checksum(&self) -> bool {
sum16(self.as_bytes()) == 0
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct FvBlockMap {
pub num_blocks: u32,
pub length: u32,
}
impl FvBlockMap {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct FirmwareVolumeExtHeader {
pub fv_name: [u8; 16], pub ext_header_size: u32,
}
impl FirmwareVolumeExtHeader {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct FirmwareVolumeExtEntry {
pub ext_entry_size: u16,
pub ext_entry_type: u32,
}
impl FirmwareVolumeExtEntry {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
pub const FIRMWARE_FILE_SYSTEM2_GUID: r_efi::base::Guid = r_efi::base::Guid::from_fields(
0x8c8ce578,
0x8a3d,
0x4f1c,
0x99,
0x35,
&[0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3],
);
pub const FIRMWARE_FILE_SYSTEM3_GUID: r_efi::base::Guid = r_efi::base::Guid::from_fields(
0x5473c07a,
0x3dcb,
0x4dca,
0xbd,
0x6f,
&[0x1e, 0x96, 0x89, 0xe7, 0x34, 0x9a],
);
pub type FvFileType = u8;
pub const FV_FILETYPE_RAW: u8 = 0x01;
pub const FV_FILETYPE_FREEFORM: u8 = 0x02;
pub const FV_FILETYPE_SECURITY_CORE: u8 = 0x03;
pub const FV_FILETYPE_PEI_CORE: u8 = 0x04;
pub const FV_FILETYPE_DXE_CORE: u8 = 0x05;
pub const FV_FILETYPE_PEIM: u8 = 0x06;
pub const FV_FILETYPE_DRIVER: u8 = 0x07;
pub const FV_FILETYPE_COMBINED_PEIM_DRIVER: u8 = 0x08;
pub const FV_FILETYPE_APPLICATION: u8 = 0x09;
pub const FV_FILETYPE_MM: u8 = 0x0A;
pub const FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: u8 = 0x0B;
pub const FV_FILETYPE_COMBINED_MM_DXE: u8 = 0x0C;
pub const FV_FILETYPE_MM_CORE: u8 = 0x0D;
pub const FV_FILETYPE_MM_STANDALONE: u8 = 0x0E;
pub const FV_FILETYPE_MM_CORE_STANDALONE: u8 = 0x0F;
pub const FV_FILETYPE_FFS_PAD: u8 = 0xF0;
pub const FFS_ATTRIB_LARGE_FILE: u8 = 0x01;
pub const FFS_ATTRIB_DATA_ALIGNMENT2: u8 = 0x02;
pub const FFS_ATTRIB_FIXED: u8 = 0x04;
pub const FFS_ATTRIB_DATA_ALIGNMENT: u8 = 0x38;
pub const FFS_ATTRIB_CHECKSUM: u8 = 0x40;
pub const FFS_FIXED_CHECKSUM: u8 = 0xAA;
pub const EFI_FILE_HEADER_CONSTRUCTION: u8 = 0x01;
pub const EFI_FILE_HEADER_VALID: u8 = 0x02;
pub const EFI_FILE_DATA_VALID: u8 = 0x04;
pub const EFI_FILE_MARKED_FOR_UPDATE: u8 = 0x08;
pub const EFI_FILE_DELETED: u8 = 0x10;
pub const EFI_FILE_HEADER_INVALID: u8 = 0x20;
pub type FfsFileAttributes = u8;
pub type FfsFileState = u8;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct Checksum {
pub header: u8,
pub file: u8,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct FfsFileHeader {
pub name: [u8; 16], pub integrity_check: Checksum,
pub r#type: FvFileType,
pub attributes: FfsFileAttributes,
pub size: [u8; 3],
pub state: FfsFileState,
}
impl FfsFileHeader {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
pub fn update_checksum(&mut self) {
self.integrity_check.header = 0;
self.integrity_check.file = 0;
self.state = 0;
self.integrity_check.header = (u8::MAX - sum8(self.as_bytes())).wrapping_add(1);
self.integrity_check.file = FFS_FIXED_CHECKSUM;
self.state = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
}
pub fn validate_checksum(&self) -> bool {
let sum = sum8(self.as_bytes());
sum ^ ((EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID)
+ FFS_FIXED_CHECKSUM)
== 0
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct FfsFileHeader2 {
pub name: Guid,
pub integrity_check: u16,
pub r#type: FvFileType,
pub attributes: FfsFileAttributes,
pub size: [u8; 3],
pub state: FfsFileState,
pub extended_size: u32,
}
impl FfsFileHeader2 {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
pub type SectionType = u8;
pub const SECTION_ALL: u8 = 0x00;
pub const SECTION_COMPRESSION: u8 = 0x01;
pub const SECTION_GUID_DEFINED: u8 = 0x02;
pub const SECTION_DISPOSABLE: u8 = 0x03;
pub const SECTION_PE32: u8 = 0x10;
pub const SECTION_PIC: u8 = 0x11;
pub const SECTION_TE: u8 = 0x12;
pub const SECTION_DXE_DEPEX: u8 = 0x13;
pub const SECTION_VERSION: u8 = 0x14;
pub const SECTION_USER_INTERFACE: u8 = 0x15;
pub const SECTION_COMPATIBILITY16: u8 = 0x16;
pub const SECTION_FIRMWARE_VOLUME_IMAGE: u8 = 0x17;
pub const SECTION_FREEFORM_SUBTYPE_GUID: u8 = 0x18;
pub const SECTION_RAW: u8 = 0x19;
pub const SECTION_PEI_DEPEX: u8 = 0x1B;
pub const SECTION_MM_DEPEX: u8 = 0x1C;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pread, Pwrite, Default)]
pub struct CommonSectionHeader {
pub size: [u8; 3],
pub r#type: SectionType,
}
impl CommonSectionHeader {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct CommonSectionHeader2 {
pub size: [u8; 3],
pub r#type: SectionType,
pub extended_size: u32,
}
impl CommonSectionHeader2 {
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fvh_checksum() {
let mut header = FirmwareVolumeHeader::default();
header.attributes = 0x4feff;
header.revision = FVH_REVISION;
header.signature = FVH_SIGNATURE;
header.header_length = size_of::<FirmwareVolumeHeader>() as u16;
header.fv_length = 0x1000;
header.checksum = 0x3fef;
header.update_checksum();
assert_eq!(header.checksum, 0x6010);
assert!(header.validate_checksum());
header.checksum = 0x3fe6;
assert!(!header.validate_checksum());
header.update_checksum();
assert_eq!(header.checksum, 0x6010);
assert!(header.validate_checksum());
}
#[test]
fn test_ffsh_checksum() {
let mut header = FfsFileHeader::default();
header.r#type = FV_FILETYPE_FFS_PAD;
header.update_checksum();
assert_eq!(header.integrity_check.header, 0x10);
assert!(header.validate_checksum());
header.name = *Guid::from_fields(
0x00000001,
0x0000,
0x0000,
0x00,
0x00,
&[0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
)
.as_bytes();
assert!(!header.validate_checksum());
header.update_checksum();
assert_eq!(header.integrity_check.header, 0xC);
assert!(header.validate_checksum());
}
#[test]
fn test_update_checksum() {
let mut header = FfsFileHeader::default();
header.update_checksum();
let mut header = FirmwareVolumeHeader::default();
header.update_checksum();
}
}