From 4b9300626310c8dd3ea6950384a8400a8378f608 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Thu, 8 Jan 2026 11:54:50 +0000 Subject: [PATCH] Link TSM-MR interface to TDX report --- kernel/src/device/misc/tdxguest.rs | 109 ++++++++++++++++++++--------- kernel/src/security/tsm_mr.rs | 60 +++++++++++++++- 2 files changed, 133 insertions(+), 36 deletions(-) diff --git a/kernel/src/device/misc/tdxguest.rs b/kernel/src/device/misc/tdxguest.rs index b646c7f35..05f2112a8 100644 --- a/kernel/src/device/misc/tdxguest.rs +++ b/kernel/src/device/misc/tdxguest.rs @@ -162,7 +162,7 @@ impl FileIo for TdxGuestFile { inblob_ptr.read()? }; - let outblob = tdx_get_report(inblob.as_bytes())?; + let outblob = tdx_get_report(Some(inblob.as_bytes()))?; let outblob_ptr = field_ptr!(&data_ptr, TdxReportRequest, tdx_report); outblob_ptr.copy_from(&SafePtr::new(&*outblob, 0))?; @@ -176,11 +176,24 @@ impl FileIo for TdxGuestFile { static TDX_REPORT: Once> = Once::new(); +#[derive(Debug, Clone, Copy)] +pub enum MeasurementReg { + MrConfigId, + MrOwner, + MrOwnerConfig, + MrTd, + Rtmr0, + Rtmr1, + Rtmr2, + Rtmr3, +} + +/// Gets the TDX quote given the specified data in `inblob`. pub fn tdx_get_quote(inblob: &[u8]) -> Result> { const GET_QUOTE_IN_FLIGHT: u64 = 0xFFFF_FFFF_FFFF_FFFF; const GET_QUOTE_BUF_SIZE: usize = 8 * 1024; - let report = tdx_get_report(inblob)?; + let report = tdx_get_report(Some(inblob))?; let report_ptr: SafePtr = SafePtr::new(&*report, 0); let buf = DmaCoherent::alloc(GET_QUOTE_BUF_SIZE.div_ceil(PAGE_SIZE), false)?; @@ -219,6 +232,52 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result> { Ok(outblob) } +/// Gets the TDX report given the specified data in `inblob`. +/// +/// The first `size_of::()` bytes of data in the returned `USegment` is the report. +/// The rest in `USegment` should be ignored. +/// If `inblob` is `None`, use the existing report data to get the report, i.e. refresh the +/// report. +pub fn tdx_get_report(inblob: Option<&[u8]>) -> Result> { + let report = TDX_REPORT.get().unwrap().write(); + + if let Some(inblob) = inblob { + if inblob.len() != size_of::() { + return_errno_with_message!(Errno::EINVAL, "Invalid inblob length"); + } + + // Use `inblob` as the data associated with the report. + report + .write_bytes(TdReport::report_data_offset(), inblob) + .unwrap(); + } + + // From TDX Module Specification, the report structure returned by TDX Module + // places the report data at offset 128, so using the same offset keeps the + // memory layout consistent with the TDX Modules's output format. And we can + // directly call `get_report` on the existing report structure without needing + // to rewrite the report data. + let report_data_ptr = report.paddr() + TdReport::report_data_offset(); + + // FIXME: The `get_report` API from the `tdx_guest` crate should have been marked `unsafe` + // because it has no way to determine if the input physical address is safe or not. + get_report(report.paddr() as u64, report_data_ptr as u64)?; + Ok(report.downgrade()) +} + +const SHA384_DIGEST_SIZE: usize = 48; + +/// Gets the measurement register value from the TDX report. +pub fn tdx_get_mr(reg: MeasurementReg) -> Result<[u8; SHA384_DIGEST_SIZE]> { + let report = TDX_REPORT.get().unwrap().read(); + + let mut blob = [0u8; SHA384_DIGEST_SIZE]; + report + .read_bytes(TdReport::mr_offset(reg), blob.as_mut()) + .unwrap(); + Ok(blob) +} + pub(super) fn init() -> Result<()> { TDX_REPORT.call_once(|| { let report = FrameAllocOptions::new() @@ -283,10 +342,10 @@ struct TdInfo { mrconfigid: [u8; 48], mrowner: [u8; 48], mrownerconfig: [u8; 48], + rtmr0: [u8; 48], rtmr1: [u8; 48], rtmr2: [u8; 48], rtmr3: [u8; 48], - rtmr4: [u8; 48], servtd_hash: [u8; 48], extension: [u8; 64], } @@ -295,6 +354,20 @@ impl TdReport { const fn report_data_offset() -> usize { offset_of!(TdReport, report_mac) + offset_of!(ReportMac, report_data) } + + const fn mr_offset(reg: MeasurementReg) -> usize { + offset_of!(TdReport, td_info) + + match reg { + MeasurementReg::MrConfigId => offset_of!(TdInfo, mrconfigid), + MeasurementReg::MrOwner => offset_of!(TdInfo, mrowner), + MeasurementReg::MrOwnerConfig => offset_of!(TdInfo, mrownerconfig), + MeasurementReg::MrTd => offset_of!(TdInfo, mrtd), + MeasurementReg::Rtmr0 => offset_of!(TdInfo, rtmr0), + MeasurementReg::Rtmr1 => offset_of!(TdInfo, rtmr1), + MeasurementReg::Rtmr2 => offset_of!(TdInfo, rtmr2), + MeasurementReg::Rtmr3 => offset_of!(TdInfo, rtmr3), + } + } } mod ioctl_defs { @@ -306,33 +379,3 @@ mod ioctl_defs { pub(super) type GetTdxReport = ioc!(TDX_CMD_GET_REPORT0, b'T', 0x01, InOutData); } - -/// Gets the TDX report given the specified data in `inblob`. -/// -/// The first `size_of::()` bytes of data in the returned `USegment` is the report. -/// The rest in `USegment` should be ignored. -fn tdx_get_report(inblob: &[u8]) -> Result> { - if inblob.len() != size_of::() { - return_errno_with_message!(Errno::EINVAL, "Invalid inblob length"); - } - - let report = TDX_REPORT.get().unwrap().write(); - - // Use `inblob` as the data associated with the report. - let report_data_paddr = { - // From TDX Module Specification, the report structure returned by TDX Module - // places the report data at offset 128, so using the same offset keeps the - // memory layout consistent with the TDX Modules's output format. And we can - // directly call `get_report` on the existing report structure without needing - // to rewrite the report data. - report - .write_bytes(TdReport::report_data_offset(), inblob) - .unwrap(); - report.paddr() + TdReport::report_data_offset() - }; - - // FIXME: The `get_report` API from the `tdx_guest` crate should have been marked `unsafe` - // because it has no way to determine if the input physical address is safe or not. - get_report(report.paddr() as u64, report_data_paddr as u64)?; - Ok(report.downgrade()) -} diff --git a/kernel/src/security/tsm_mr.rs b/kernel/src/security/tsm_mr.rs index 42f7dc643..83bba9bb7 100644 --- a/kernel/src/security/tsm_mr.rs +++ b/kernel/src/security/tsm_mr.rs @@ -7,7 +7,12 @@ use aster_systree::{ inherit_sys_branch_node, inherit_sys_leaf_node, }; use inherit_methods_macro::inherit_methods; -use ostd::mm::{VmReader, VmWriter}; +use ostd::{ + mm::{FallibleVmWrite, VmReader, VmWriter}, + sync::RwMutex, +}; + +use crate::device::misc::tdxguest::{MeasurementReg, tdx_get_mr, tdx_get_report}; pub(super) fn init() { let node = { @@ -66,46 +71,71 @@ inherit_sys_branch_node!(TdxGuestSysNodeRoot, fields, { #[derive(Debug)] struct Measurement { fields: NormalNodeFields, + in_sync: RwMutex, } #[derive(Debug)] struct MeasurementAttr { name: &'static str, perms: SysPerms, + reg: MeasurementReg, + refresh_on_read: bool, +} + +impl MeasurementAttr { + fn refresh_on_read(&self) -> bool { + self.refresh_on_read + } } const MEASUREMENT_ATTRS: &[MeasurementAttr] = &[ MeasurementAttr { name: "mrconfigid", perms: SysPerms::DEFAULT_RO_ATTR_PERMS, + reg: MeasurementReg::MrConfigId, + refresh_on_read: false, }, MeasurementAttr { name: "mrowner", perms: SysPerms::DEFAULT_RO_ATTR_PERMS, + reg: MeasurementReg::MrOwner, + refresh_on_read: false, }, MeasurementAttr { name: "mrownerconfig", perms: SysPerms::DEFAULT_RO_ATTR_PERMS, + reg: MeasurementReg::MrOwnerConfig, + refresh_on_read: false, }, MeasurementAttr { name: "mrtd:sha384", perms: SysPerms::DEFAULT_RO_ATTR_PERMS, + reg: MeasurementReg::MrTd, + refresh_on_read: false, }, MeasurementAttr { name: "rtmr0:sha384", perms: SysPerms::DEFAULT_RW_ATTR_PERMS, + reg: MeasurementReg::Rtmr0, + refresh_on_read: true, }, MeasurementAttr { name: "rtmr1:sha384", perms: SysPerms::DEFAULT_RW_ATTR_PERMS, + reg: MeasurementReg::Rtmr1, + refresh_on_read: true, }, MeasurementAttr { name: "rtmr2:sha384", perms: SysPerms::DEFAULT_RW_ATTR_PERMS, + reg: MeasurementReg::Rtmr2, + refresh_on_read: true, }, MeasurementAttr { name: "rtmr3:sha384", perms: SysPerms::DEFAULT_RW_ATTR_PERMS, + reg: MeasurementReg::Rtmr3, + refresh_on_read: true, }, ]; @@ -120,7 +150,10 @@ impl Measurement { Arc::new_cyclic(|weak_self| { let fields = NormalNodeFields::new(name, attrs, weak_self.clone()); - Measurement { fields } + Measurement { + fields, + in_sync: RwMutex::new(false), + } }) } } @@ -131,7 +164,28 @@ inherit_sys_leaf_node!(Measurement, fields, { } fn read_attr_at(&self, name: &str, offset: usize, writer: &mut VmWriter) -> Result { - Err(Error::AttributeError) + let attr = MEASUREMENT_ATTRS + .iter() + .find(|attr| attr.name == name) + .unwrap(); + + let mut in_sync = self.in_sync.upread(); + + if attr.refresh_on_read() && !*in_sync { + let mut in_sync_write = in_sync.upgrade(); + if !*in_sync_write { + tdx_get_report(None).map_err(|_| Error::AttributeError)?; + *in_sync_write = true; + } + in_sync = in_sync_write.downgrade(); + } + + let mr = tdx_get_mr(attr.reg).map_err(|_| Error::AttributeError)?; + + let mut reader = VmReader::from(&mr[offset..]); + writer + .write_fallible(&mut reader) + .map_err(|_| Error::AttributeError) } fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result {