From 896acada3321c64ba2c2a1403093ebf0a555937f Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 30 Dec 2024 15:20:21 +0800 Subject: [PATCH] AA/attester: support to read CCEL for kernel older than v6.4 linux kernels older than v6.4 does not support to read CCEL from acpi sysfs. This patch supports to read CCEL from dev/mem This is accomplished by reading CCEL's physical address from ACPI table description. Then read the CCEL from /dev/mem. ACPI table to include CCEL patch of linux kernel https://github.com/torvalds/linux/commit/4f855dcead6c5be0a48a2779eeecb170ec144534 Signed-off-by: Xynnn007 --- attestation-agent/attester/Cargo.toml | 2 +- attestation-agent/attester/src/tdx/ccel.rs | 104 +++++++++++++++++++++ attestation-agent/attester/src/tdx/mod.rs | 4 +- 3 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 attestation-agent/attester/src/tdx/ccel.rs 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);