const PLATFORM_UEFI: u8 = 0xEF;
const BOOTABLE: u8 = 0x88;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UefiImage {
pub lba: u32,
pub sectors: u32,
}
pub fn parse_catalog(cat: &[u8]) -> Option<UefiImage> {
if cat.len() < 64 {
return None;
}
if cat[0] != 0x01 || cat[30] != 0x55 || cat[31] != 0xAA {
return None;
}
if cat[1] == PLATFORM_UEFI {
return parse_entry(&cat[32..64]);
}
let mut off = 64;
while off + 32 <= cat.len() {
let hdr = &cat[off..off + 32];
let indicator = hdr[0]; if indicator != 0x90 && indicator != 0x91 {
break;
}
let platform = hdr[1];
let n_entries = u16::from_le_bytes([hdr[2], hdr[3]]) as usize;
off += 32;
if platform == PLATFORM_UEFI {
for _ in 0..n_entries {
if off + 32 > cat.len() {
break;
}
if let Some(img) = parse_entry(&cat[off..off + 32]) {
return Some(img);
}
off += 32;
}
} else {
off = off.saturating_add(n_entries.saturating_mul(32));
}
if indicator == 0x91 {
break;
}
}
None
}
fn parse_entry(e: &[u8]) -> Option<UefiImage> {
if e.len() < 12 || e[0] != BOOTABLE {
return None;
}
let sectors = u32::from(u16::from_le_bytes([e[6], e[7]]));
let lba = u32::from_le_bytes([e[8], e[9], e[10], e[11]]);
Some(UefiImage { lba, sectors })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_validation_uefi_default_entry() {
let mut cat = [0u8; 96];
cat[0] = 0x01; cat[1] = 0xEF; cat[30] = 0x55;
cat[31] = 0xAA; cat[32] = 0x88; cat[38] = 0x04; cat[40] = 0x22; assert_eq!(
parse_catalog(&cat),
Some(UefiImage {
lba: 34,
sectors: 4
})
);
}
#[test]
fn finds_uefi_section_after_bios_default() {
let mut cat = [0u8; 128];
cat[0] = 0x01;
cat[1] = 0x00; cat[30] = 0x55;
cat[31] = 0xAA;
cat[32] = 0x88; cat[64] = 0x91;
cat[65] = 0xEF;
cat[66] = 0x01;
cat[96] = 0x88;
cat[96 + 6] = 0x08; cat[96 + 8] = 0x40; assert_eq!(
parse_catalog(&cat),
Some(UefiImage {
lba: 64,
sectors: 8
})
);
}
#[test]
fn no_uefi_entry_returns_none() {
let mut cat = [0u8; 64];
cat[0] = 0x01;
cat[1] = 0x00;
cat[30] = 0x55;
cat[31] = 0xAA;
assert_eq!(parse_catalog(&cat), None);
}
}