diff --git a/attestation-agent/attester/Cargo.toml b/attestation-agent/attester/Cargo.toml index f371e3a06..3c0f20636 100644 --- a/attestation-agent/attester/Cargo.toml +++ b/attestation-agent/attester/Cargo.toml @@ -59,7 +59,7 @@ all-attesters = [ # quotes. It's an unconditional dependency for tdx-attester since that is the only way to # generate TDX quotes with upstream kernels. tsm-report = ["tempfile"] -tdx-attester = ["scroll", "tsm-report", "tdx-attest-rs"] +tdx-attester = ["scroll", "tsm-report", "tdx-attest-rs", "tokio/io-util"] sgx-attester = ["occlum_dcap"] az-snp-vtpm-attester = ["az-snp-vtpm"] az-tdx-vtpm-attester = ["az-snp-vtpm-attester", "az-tdx-vtpm"] diff --git a/attestation-agent/attester/src/tdx/ccel.rs b/attestation-agent/attester/src/tdx/ccel.rs new file mode 100644 index 000000000..fc1e6bf5f --- /dev/null +++ b/attestation-agent/attester/src/tdx/ccel.rs @@ -0,0 +1,104 @@ +// Copyright (c) 2024 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{bail, Context}; +use scroll::Pread; +use tokio::io::{AsyncReadExt, AsyncSeekExt}; + +use crate::tdx::TdxAttester; + +use std::path::Path; + +const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; + +/// Path to the ACPI table CCEL description +const CCEL_ACPI_DESCRIPTION: &str = "/sys/firmware/acpi/tables/CCEL"; + +/// Guest memory which is used to read the CCEL +const GUEST_MEMORY: &str = "/dev/mem"; + +/// Signature of CCEL's ACPI Description Header +const CCEL_SIGNATURE: &[u8] = b"CCEL"; + +#[repr(C)] +#[derive(Pread)] +struct EfiAcpiDescriptionHeader { + signature: u32, + length: u32, + revision: u8, + checksum: u8, + oem_id: [u8; 6], + oem_table_id: u64, + oem_revision: u32, + craetor_id: u32, + creator_revision: u32, +} + +#[repr(C)] +#[derive(Pread)] +struct TdxEventLogACPITable { + efi_acpi_description_header: EfiAcpiDescriptionHeader, + rsv: u32, + laml: u64, + lasa: u64, +} + +impl TdxAttester { + pub async fn read_ccel() -> anyhow::Result> { + if Path::new(CCEL_PATH).exists() { + let ccel = tokio::fs::read(CCEL_PATH).await?; + return Ok(ccel); + } + + let efi_acpi_description = tokio::fs::read(CCEL_ACPI_DESCRIPTION) + .await + .context("read ccel description")?; + let ccel_acpi_table = efi_acpi_description + .pread::(0) + .context("parse CCEL ACPI description failed")?; + + let ccel_signature = u32::from_le_bytes(CCEL_SIGNATURE.try_into()?); + if ccel_acpi_table.efi_acpi_description_header.signature != ccel_signature { + bail!("invalid CCEL ACPI table: wrong CCEL signature"); + } + + if ccel_acpi_table.rsv != 0 { + bail!("invalid CCEL ACPI table: RSV must be 0"); + } + + if ccel_acpi_table.efi_acpi_description_header.length != efi_acpi_description.len() as u32 { + bail!("invalid CCEL ACPI table: header length not match"); + } + + let mut guest_memory = tokio::fs::OpenOptions::new() + .read(true) + .open(GUEST_MEMORY) + .await?; + guest_memory + .seek(std::io::SeekFrom::Start(ccel_acpi_table.lasa)) + .await?; + let mut ccel = vec![0; ccel_acpi_table.laml as usize]; + let read_size = guest_memory.read(&mut ccel).await?; + if read_size == 0 { + bail!("read CCEL failed"); + } + + Ok(ccel) + } +} + +#[cfg(test)] +mod tests { + use crate::tdx::TdxAttester; + + #[ignore] + #[tokio::test] + async fn test_read_ccel() { + let ccel = TdxAttester::read_ccel().await.unwrap(); + tokio::fs::write("/root/test/guest-components/2.bin", ccel) + .await + .unwrap(); + } +} diff --git a/attestation-agent/attester/src/tdx/mod.rs b/attestation-agent/attester/src/tdx/mod.rs index 70507876d..b57d36774 100644 --- a/attestation-agent/attester/src/tdx/mod.rs +++ b/attestation-agent/attester/src/tdx/mod.rs @@ -18,11 +18,11 @@ use std::fs; use std::path::Path; use tdx_attest_rs::tdx_report_t; +mod ccel; mod report; mod rtmr; const TDX_REPORT_DATA_SIZE: usize = 64; -const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; pub fn detect_platform() -> bool { TsmReportPath::new(TsmReportProvider::Tdx).is_ok() || Path::new("/dev/tdx_guest").exists() @@ -129,7 +129,7 @@ impl Attester for TdxAttester { let engine = base64::engine::general_purpose::STANDARD; let quote = engine.encode(quote_bytes); - let cc_eventlog = match std::fs::read(CCEL_PATH) { + let cc_eventlog = match Self::read_ccel().await { Result::Ok(el) => Some(engine.encode(el)), Result::Err(e) => { log::warn!("Read CC Eventlog failed: {:?}", e);