use anyhow::{bail, Context, Result};
use byteorder::{LittleEndian, ReadBytesExt};
use std::{
fs,
io::{Read, Seek},
path::Path,
};
pub const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL";
pub const CCEL_ACPI_DESCRIPTION: &str = "/sys/firmware/acpi/tables/CCEL";
pub const GUEST_MEMORY: &str = "/dev/mem";
pub const CCEL_SIGNATURE: &[u8] = b"CCEL";
pub fn read_ccel() -> Result<Vec<u8>> {
if Path::new(CCEL_PATH).exists() {
let ccel = fs::read(CCEL_PATH)?;
return Ok(ccel);
}
let efi_acpi_description =
fs::read(CCEL_ACPI_DESCRIPTION).context("ccel description does not exist")?;
if efi_acpi_description.len() < 56 {
bail!("invalid CCEL ACPI description");
}
let mut index = 0;
let signature = (&efi_acpi_description[index..index + 4]).read_u32::<LittleEndian>()?;
index += 4;
let length = (&efi_acpi_description[index..index + 4]).read_u32::<LittleEndian>()?;
index += 32;
let rsv = (&efi_acpi_description[index..index + 4]).read_u32::<LittleEndian>()?;
index += 4;
let laml = (&efi_acpi_description[index..index + 8]).read_u64::<LittleEndian>()?;
index += 8;
let lasa = (&efi_acpi_description[index..index + 8]).read_u64::<LittleEndian>()?;
let ccel_signature = u32::from_le_bytes(CCEL_SIGNATURE.try_into()?);
if signature != ccel_signature {
bail!("invalid CCEL ACPI table: wrong CCEL signature");
}
if length != efi_acpi_description.len() as u32 {
bail!("invalid CCEL ACPI table: header length not match");
}
let mut guest_memory = fs::OpenOptions::new().read(true).open(GUEST_MEMORY)?;
guest_memory.seek(std::io::SeekFrom::Start(lasa))?;
let mut ccel = vec![0; laml as usize];
let read_size = guest_memory.read(&mut ccel)?;
if read_size == 0 {
bail!("read CCEL failed");
}
Ok(ccel)
}
#[cfg(test)]
mod tests {
use super::read_ccel;
#[ignore]
#[test]
fn test_read_ccel() {
let _ccel = read_ccel().unwrap();
}
}