#![allow(non_snake_case)]
use std::{
fmt::{Debug, Display},
ops::Range,
};
use bitfield_struct::bitfield;
use serde::{Deserialize, Serialize};
use zerocopy::{FromBytes, IntoBytes};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes};
use crate::{EMPTY, me::Generation, ver::Version};
const MAGIC: u32 = 0x0ff0_a55a;
const SIZE: usize = 0x800;
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FLMAP0 {
FCBA: u8,
#[bits(2)]
NC: u8,
#[bits(6)]
_0: u8,
FRBA: u8,
#[bits(3)]
NR: u8,
#[bits(5)]
_1: u8,
}
impl FLMAP0 {
fn fcba(self) -> usize {
(self.FCBA() as usize) << 4
}
fn nc(self) -> usize {
self.NC() as usize + 1
}
fn frba(self) -> usize {
(self.FRBA() as usize) << 4
}
fn nr(self) -> usize {
self.NR() as usize + 1
}
}
impl Display for FLMAP0 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fcba = self.fcba();
let nc = self.nc();
let frba = self.frba();
let nr = self.nr();
let c = format!(" components: {nc}, base: 0x{fcba:08x}");
let r = format!(" regions: {nr}, base: 0x{frba:08x}");
write!(f, "{c}\n{r}")
}
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FLMAP1 {
FMBA: u8,
#[bits(3)]
NM: u8,
#[bits(5)]
_0: u8,
FISBA: u8,
ISL: u8,
}
impl FLMAP1 {
fn fmba(self) -> usize {
(self.FMBA() as usize) << 4
}
fn nm(self) -> usize {
self.NM() as usize + 1
}
fn fisba(self) -> usize {
(self.FISBA() as usize) << 4
}
fn isl(self) -> usize {
self.ISL() as usize
}
}
impl Display for FLMAP1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fmba = self.fmba();
let nm = self.nm();
let fisba = self.fisba();
let isl = self.isl();
let m = format!(" masters: {nm}, base: 0x{fmba:08x}");
let s = format!(" ICH8/PCH straps: {isl:2}, base: 0x{fisba:08x}");
write!(f, "{m}\n{s}")
}
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FLMAP2 {
FMSBA: u8,
MSL: u8,
_0: u16,
}
impl FLMAP2 {
fn fmsba(self) -> usize {
(self.FMSBA() as usize) << 4
}
fn msl(self) -> usize {
self.MSL() as usize
}
}
impl Display for FLMAP2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fmsba = self.fmsba();
let msl = self.msl();
write!(f, " MCH straps: {msl:2}, base: 0x{fmsba:08x}")
}
}
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize, Clone, Copy, Debug)]
#[repr(C)]
pub struct Header {
magic: u32,
flmap0: FLMAP0,
flmap1: FLMAP1,
flmap2: FLMAP2, flmap3: u32, }
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
enum Density {
K512,
M1,
M2,
M4,
M8,
M16,
_Undefined,
_Reserved,
}
impl Density {
const fn from_bits(val: u8) -> Self {
match val {
0b000 => Self::K512,
0b001 => Self::M1,
0b010 => Self::M2,
0b011 => Self::M4,
0b100 => Self::M8,
0b101 => Self::M16,
0b111 => Self::_Reserved,
_ => Self::_Undefined,
}
}
const fn into_bits(self) -> u8 {
self as u8
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
enum Frequency {
M20,
M33,
M48,
M50_30,
M17,
_Undefined,
_Reserved,
}
impl Frequency {
const fn from_bits(val: u8) -> Self {
match val {
0b000 => Self::M20,
0b001 => Self::M33,
0b010 => Self::M48,
0b011 => Self::_Undefined,
0b100 => Self::M50_30,
0b101 => Self::_Undefined,
0b110 => Self::M17,
_ => Self::_Reserved,
}
}
const fn into_bits(self) -> u8 {
self as u8
}
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FlashComponentConfig {
#[bits(3)]
comp1_density: Density,
#[bits(3)]
comp2_density: Density,
#[bits(2)]
_r: u8,
#[bits(8)]
_r: u8,
#[bits(1)]
_r: u8,
#[bits(3)]
read_clock_frequency: Frequency,
fast_read_support: bool,
#[bits(3)]
fast_read_clock_frequency: Frequency,
#[bits(3)]
write_erase_clock_frequency: Frequency,
#[bits(3)]
read_id_status_clock_frequency: Frequency,
#[bits(2)]
_r: u8,
}
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize, Clone, Copy, Debug)]
pub struct FlashInvalidInstructions {
inst1: u8,
inst2: u8,
inst3: u8,
inst4: u8,
}
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize, Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct Components {
FLCOMP: FlashComponentConfig,
FLILL0: FlashInvalidInstructions,
FLILL1: FlashInvalidInstructions,
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FlashRegion {
#[bits(13)]
base: u32,
#[bits(3)]
_0: u8,
#[bits(13)]
limit: u32,
#[bits(3)]
_1: u8,
}
impl FlashRegion {
fn ba(self) -> usize {
self.base() as usize * 4096
}
fn la(self) -> usize {
self.limit() as usize * 4096 + 4095
}
pub fn range(self) -> Range<usize> {
self.ba()..self.la() + 1
}
}
impl Display for FlashRegion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let b = self.ba();
let l = self.la();
let u = if b > l { " (unused)" } else { "" };
write!(f, "{b:08x} - {l:08x}{u}")
}
}
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize, Clone, Copy, Debug)]
#[repr(C)]
pub struct Regions {
pub flreg0: FlashRegion,
pub flreg1: FlashRegion,
pub flreg2: FlashRegion,
pub flreg3: FlashRegion,
pub flreg4: FlashRegion,
pub flreg5: FlashRegion,
pub flreg6: FlashRegion,
pub flreg7: FlashRegion,
pub flreg8: FlashRegion,
pub flreg9: FlashRegion,
}
impl Regions {
pub fn ifd_range(&self) -> Range<usize> {
self.flreg0.range()
}
pub fn bios_range(&self) -> Range<usize> {
self.flreg1.range()
}
pub fn me_range(&self) -> Range<usize> {
self.flreg2.range()
}
}
impl Display for Regions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let r0 = format!(" Flash descriptor (IFD): {}", self.flreg0);
let r1 = format!(" BIOS (host) firmware: {}", self.flreg1);
let r2 = format!(" (CS)ME firmware: {}", self.flreg2);
let r3 = format!(" Gigabit ethernet data: {}", self.flreg3);
let r4 = format!(" Platform data: {}", self.flreg4);
let r5 = format!(" Embedded controller (EC): {}", self.flreg5);
write!(f, "{r0}\n{r1}\n{r2}\n{r3}\n{r4}\n{r5}")
}
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FlashMasterV1 {
_r: u16,
read_fd: bool,
read_bios: bool,
read_me: bool,
read_gbe: bool,
read_pd: bool,
#[bits(3)]
_r: u8,
write_fd: bool,
write_bios: bool,
write_me: bool,
write_gbe: bool,
write_pd: bool,
#[bits(3)]
_r: u8,
}
fn cap_to_str(cap: bool) -> &'static str {
if cap { "enabled" } else { "disabled" }
}
impl Display for FlashMasterV1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c = cap_to_str(self.write_pd());
writeln!(f, " Platform Data Region Write Access: {c}")?;
let c = cap_to_str(self.write_gbe());
writeln!(f, " GbE Region Write Access: {c}")?;
let c = cap_to_str(self.write_me());
writeln!(f, " Intel ME Region Write Access: {c}")?;
let c = cap_to_str(self.write_bios());
writeln!(f, " Host CPU/BIOS Region Write Access: {c}")?;
let c = cap_to_str(self.write_fd());
writeln!(f, " Flash Descriptor Write Access: {c}")?;
let c = cap_to_str(self.read_pd());
writeln!(f, " Platform Data Region Read Access: {c}")?;
let c = cap_to_str(self.read_gbe());
writeln!(f, " GbE Region Read Access: {c}")?;
let c = cap_to_str(self.read_me());
writeln!(f, " Intel ME Region Read Access: {c}")?;
let c = cap_to_str(self.read_bios());
writeln!(f, " Host CPU/BIOS Region Read Access: {c}")?;
let c = cap_to_str(self.read_fd());
writeln!(f, " Flash Descriptor Read Access: {c}")?;
write!(f, "")
}
}
#[bitfield(u32)]
#[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize)]
pub struct FlashMasterV2 {
read_gbe10_1: bool,
#[bits(3)]
_r: u8,
write_gbe10_1: bool,
#[bits(3)]
_r: u8,
read_fd: bool,
read_bios: bool,
read_me: bool,
read_gbe: bool,
read_pd: bool,
#[bits(3)]
_r: u8,
read_ec: bool,
#[bits(2)]
_r: u8,
read_gbe10_0: bool,
write_fd: bool,
write_bios: bool,
write_me: bool,
write_gbe: bool,
write_pd: bool,
#[bits(3)]
_r: u8,
write_ec: bool,
#[bits(2)]
_r: u8,
write_gbe10_0: bool,
}
impl Display for FlashMasterV2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c = cap_to_str(self.write_ec());
writeln!(f, " EC Region Write Access: {c}")?;
let c = cap_to_str(self.write_pd());
writeln!(f, " Platform Data Region Write Access: {c}")?;
let c = cap_to_str(self.write_gbe());
writeln!(f, " GbE Region Write Access: {c}")?;
let c = cap_to_str(self.write_me());
writeln!(f, " Intel ME Region Write Access: {c}")?;
let c = cap_to_str(self.write_bios());
writeln!(f, " Host CPU/BIOS Region Write Access: {c}")?;
let c = cap_to_str(self.write_fd());
writeln!(f, " Flash Descriptor Write Access: {c}")?;
let c = cap_to_str(self.read_ec());
writeln!(f, " EC Region Read Access: {c}")?;
let c = cap_to_str(self.read_pd());
writeln!(f, " Platform Data Region Read Access: {c}")?;
let c = cap_to_str(self.read_gbe());
writeln!(f, " GbE Region Read Access: {c}")?;
let c = cap_to_str(self.read_me());
writeln!(f, " Intel ME Region Read Access: {c}")?;
let c = cap_to_str(self.read_bios());
writeln!(f, " Host CPU/BIOS Region Read Access: {c}")?;
let c = cap_to_str(self.read_fd());
writeln!(f, " Flash Descriptor Read Access: {c}")?;
write!(f, "")
}
}
#[derive(Serialize, Deserialize, Clone)]
#[repr(C)]
pub struct IFD {
pub header: Header,
pub components: Components,
pub regions: Regions,
pub masters: Vec<u32>,
pub pch_straps: Vec<u32>,
pub mch_straps: Vec<u32>,
}
impl Display for IFD {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "== Configuration ==")?;
writeln!(f, "{}", self.header.flmap0)?;
writeln!(f, "{}", self.header.flmap1)?;
writeln!(f, "{}", self.header.flmap2)?;
writeln!(f, "== Components ==")?;
writeln!(f, "{:#02x?}", self.components)?;
writeln!(f, "== Regions ==")?;
write!(f, "{}", self.regions)
}
}
impl Debug for IFD {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{self}")?;
writeln!(f, "== ICH/PCH Straps ==")?;
for (i, s) in self.pch_straps.iter().enumerate() {
writeln!(f, " {i:2}: {s:08x}")?;
}
writeln!(f, "== MCH Straps ==")?;
for (i, s) in self.mch_straps.iter().enumerate() {
writeln!(f, " {i:2}: {s:08x}")?;
}
write!(f, "")
}
}
fn extract_bit(straps: &[u32], byte: usize, bit: u32) -> bool {
straps[byte] >> bit & 1 == 1
}
fn set_bit(straps: &mut [u32], byte: usize, bit: u32, s: bool) {
let b = straps[byte];
straps[byte] = b & !(1 << bit) | (u32::from(s) << bit);
}
impl IFD {
pub fn dci(&self) -> bool {
extract_bit(&self.pch_straps, 0, 17)
}
pub fn set_dci(&mut self, s: bool) {
set_bit(&mut self.pch_straps, 0, 17, s);
}
pub fn hap(&self) -> bool {
extract_bit(&self.pch_straps, 0, 16)
}
pub fn set_hap(&mut self, s: bool) {
set_bit(&mut self.pch_straps, 0, 16, s);
}
pub fn ich_me_disabled(&self) -> bool {
extract_bit(&self.pch_straps, 0, 0)
}
pub fn set_ich_me_disabled(&mut self, s: bool) {
set_bit(&mut self.pch_straps, 0, 0, s);
}
pub fn mch_me_disabled(&self) -> bool {
extract_bit(&self.mch_straps, 0, 0)
}
pub fn set_mch_me_disabled(&mut self, s: bool) {
set_bit(&mut self.mch_straps, 0, 0, s);
}
pub fn mch_alt_me_disabled(&self) -> bool {
extract_bit(&self.mch_straps, 0, 7)
}
pub fn set_mch_alt_me_disabled(&mut self, s: bool) {
set_bit(&mut self.mch_straps, 0, 7, s);
}
pub fn alt_me_disabled(&self) -> bool {
extract_bit(&self.pch_straps, 10, 7)
}
pub fn set_alt_me_disabled(&mut self, s: bool) {
set_bit(&mut self.pch_straps, 10, 7, s);
}
pub fn disable_me_gen3(&mut self, me_ver: &Option<Version>) -> Result<(), String> {
if let Some(ver) = me_ver {
match ver.major {
11 => self.set_hap(true),
_ => return Err("ME > 11 not yet supported".into()),
}
}
Ok(())
}
pub fn disable_me(
&mut self,
me_gen: &Generation,
me_ver: &Option<Version>,
) -> Result<(), String> {
match me_gen {
Generation::Gen1 => {
self.set_ich_me_disabled(true);
self.set_mch_me_disabled(true);
Ok(())
}
Generation::Gen2 => {
self.set_alt_me_disabled(true);
Ok(())
}
Generation::Gen3 => self.disable_me_gen3(me_ver),
_ => Err("Unknown ME generation/version".into()),
}
}
}
impl IFD {
pub fn to_vec(self) -> Vec<u8> {
let mut res = vec![EMPTY; SIZE];
let components_offset = self.header.flmap0.fcba();
let regions_offset = self.header.flmap0.frba();
let masters_offset = self.header.flmap1.fmba();
let pch_straps_offset = self.header.flmap1.fisba();
let mch_straps_offset = self.header.flmap2.fmsba();
for (o, b) in self.header.as_bytes().iter().enumerate() {
res[OFFSET + o] = *b;
}
for (o, b) in self.components.as_bytes().iter().enumerate() {
res[components_offset + o] = *b;
}
for (o, b) in self.regions.as_bytes().iter().enumerate() {
res[regions_offset + o] = *b;
}
for (o, b) in self.masters.as_bytes().iter().enumerate() {
res[masters_offset + o] = *b;
}
for (o, b) in self.pch_straps.as_bytes().iter().enumerate() {
res[pch_straps_offset + o] = *b;
}
for (o, b) in self.mch_straps.as_bytes().iter().enumerate() {
res[mch_straps_offset + o] = *b;
}
res
}
}
const OFFSET: usize = 16;
const REGION_COUNT: usize = 8;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum IfdError {
NoIfd(String),
DataTooSmall(String),
ParserError(String),
OutOfBounds(String),
}
fn u8_slice_to_u32(slice: &[u8]) -> Vec<u32> {
slice
.chunks(4)
.map(|v| u32::from_le_bytes([v[0], v[1], v[2], v[3]]))
.collect()
}
impl IFD {
pub fn parse(data: &[u8]) -> Result<Self, IfdError> {
if data.len() < OFFSET {
return Err(IfdError::DataTooSmall(
"Not enough data: need at least 4096 bytes".into(),
));
}
let header = match Header::read_from_prefix(&data[OFFSET..]) {
Ok((r, _)) => r,
Err(e) => return Err(IfdError::ParserError(format!("IFD header: {e:?}"))),
};
if header.magic != MAGIC {
return Err(IfdError::NoIfd(format!(
"IFD magic not as expected, got: {:08x}, wanted: {MAGIC:08x}",
header.magic
)));
}
let components_offset = header.flmap0.fcba();
let regions_offset = header.flmap0.frba();
let masters_offset = header.flmap1.fmba();
let pch_straps_offset = header.flmap1.fisba();
let mch_straps_offset = header.flmap2.fmsba();
if components_offset > data.len() {
return Err(IfdError::OutOfBounds(format!(
"Components @ {components_offset:08x}"
)));
}
let components = match Components::read_from_prefix(&data[components_offset..]) {
Ok((r, _)) => r,
Err(e) => return Err(IfdError::ParserError(format!("{e:?}"))),
};
if regions_offset > data.len() {
return Err(IfdError::OutOfBounds(format!(
"Regions @ {regions_offset:08x}"
)));
}
let regions = match Regions::read_from_prefix(&data[regions_offset..]) {
Ok((r, _)) => r,
Err(e) => return Err(IfdError::ParserError(format!("{e:?}"))),
};
let masters_end = masters_offset + REGION_COUNT * 4;
if masters_offset > data.len() || masters_end > data.len() {
return Err(IfdError::OutOfBounds(format!(
"Masters @ {masters_offset:08x}..{masters_end:08x}"
)));
}
let slice = &data[masters_offset..masters_end];
let masters = u8_slice_to_u32(slice);
let pch_straps_end = pch_straps_offset + header.flmap1.isl() * 4;
if pch_straps_offset > data.len() || pch_straps_end > data.len() {
return Err(IfdError::OutOfBounds(format!(
"PCH straps @ {pch_straps_offset:08x}..{pch_straps_end:08x}"
)));
}
let slice = &data[pch_straps_offset..pch_straps_end];
let pch_straps = u8_slice_to_u32(slice);
let mch_straps_end = mch_straps_offset + header.flmap2.msl() * 4;
if mch_straps_offset > data.len() || mch_straps_end > data.len() {
return Err(IfdError::OutOfBounds(format!(
"MCH straps @ {mch_straps_offset:08x}..{mch_straps_end:08x}"
)));
}
let slice = &data[mch_straps_offset..mch_straps_end];
let mch_straps = u8_slice_to_u32(slice);
Ok(Self {
header,
components,
regions,
masters,
pch_straps,
mch_straps,
})
}
}
#[cfg(test)]
static IFD_DATA_GEN2: &[u8] = include_bytes!("../tests/me8.ifd");
#[cfg(test)]
static IFD_DATA_GEN3: &[u8] = include_bytes!("../tests/me11.ifd");
#[test]
fn to_vec_gen2() {
let ifd = IFD::parse(IFD_DATA_GEN2).unwrap();
assert_eq!(ifd.to_vec(), IFD_DATA_GEN2);
}
#[test]
fn to_vec_gen3() {
let ifd = IFD::parse(IFD_DATA_GEN3).unwrap();
assert_eq!(ifd.to_vec(), IFD_DATA_GEN3);
}