diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index 9a52865..a91b666 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -344,6 +344,8 @@ pub enum IgvmInitializationHeader { vp_index: u16, vtl: Vtl, }, + /// Represents an [IGVM_VHS_TD_INFO]. + TdInfo { compatibility_mask: u32, xfam: u64 }, } impl IgvmInitializationHeader { @@ -357,6 +359,7 @@ impl IgvmInitializationHeader { IgvmInitializationHeader::PageTableRelocationRegion { .. } => { size_of::() } + IgvmInitializationHeader::TdInfo { .. } => size_of::(), }; size_of::() + additional @@ -376,6 +379,7 @@ impl IgvmInitializationHeader { IgvmInitializationHeader::PageTableRelocationRegion { .. } => { IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION } + IgvmInitializationHeader::TdInfo { .. } => IgvmVariableHeaderType::IGVM_VHT_TD_INFO, } } @@ -454,6 +458,10 @@ impl IgvmInitializationHeader { Ok(()) } + IgvmInitializationHeader::TdInfo { + compatibility_mask: _, + xfam: _, + } => Ok(()), } } @@ -550,6 +558,22 @@ impl IgvmInitializationHeader { vtl: vtl.try_into().map_err(|_| BinaryHeaderError::InvalidVtl)?, } } + IgvmVariableHeaderType::IGVM_VHT_TD_INFO if length == size_of::() => { + let IGVM_VHS_TD_INFO { + compatibility_mask, + reserved, + xfam, + } = read_header(&mut variable_headers)?; + + if reserved != 0 { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmInitializationHeader::TdInfo { + compatibility_mask, + xfam, + } + } _ => return Err(BinaryHeaderError::InvalidVariableHeaderType), }; @@ -572,6 +596,9 @@ impl IgvmInitializationHeader { PageTableRelocationRegion { compatibility_mask, .. } => Some(*compatibility_mask), + TdInfo { + compatibility_mask, .. + } => Some(*compatibility_mask), } } @@ -665,6 +692,22 @@ impl IgvmInitializationHeader { variable_headers, ); } + IgvmInitializationHeader::TdInfo { + compatibility_mask, + xfam, + } => { + let info = IGVM_VHS_TD_INFO { + compatibility_mask: *compatibility_mask, + reserved: 0, + xfam: *xfam, + }; + + append_header( + &info, + IgvmVariableHeaderType::IGVM_VHT_TD_INFO, + variable_headers, + ); + } } Ok(()) @@ -2348,7 +2391,7 @@ impl IgvmFile { fn validate_platform_headers<'a>( revision: IgvmRevision, platform_headers: impl Iterator, - ) -> Result<(), Error> { + ) -> Result, Error> { let mut at_least_one = false; let mut isolation_types = HashMap::new(); @@ -2392,7 +2435,7 @@ impl IgvmFile { if !at_least_one { Err(Error::NoPlatformHeaders) } else { - Ok(()) + Ok(isolation_types) } } @@ -2402,6 +2445,7 @@ impl IgvmFile { fn validate_initialization_headers( revision: IgvmRevision, initialization_headers: &[IgvmInitializationHeader], + isolation_types: HashMap, ) -> Result { let mut page_table_masks = 0; let mut used_vp_idents: Vec = Vec::new(); @@ -2507,6 +2551,20 @@ impl IgvmFile { vtl: *vtl, }) } + IgvmInitializationHeader::TdInfo { + compatibility_mask, + xfam: _, + } => { + if !isolation_types.contains_key(&IgvmPlatformType::TDX) + || isolation_types + .get(&IgvmPlatformType::TDX) + .unwrap() + .compatibility_mask + != *compatibility_mask + { + return Err(Error::InvalidPlatformType); + } + } // TODO: validate SNP policy compatibility mask specifies SNP _ => {} } @@ -2792,9 +2850,12 @@ impl IgvmFile { return Err(Error::UnsupportedPageSize(revision.page_size() as u32)); } - Self::validate_platform_headers(revision, platform_headers.iter())?; - let validation_info = - Self::validate_initialization_headers(revision, &initialization_headers)?; + let isolation_types = Self::validate_platform_headers(revision, platform_headers.iter())?; + let validation_info = Self::validate_initialization_headers( + revision, + &initialization_headers, + isolation_types, + )?; Self::validate_directive_headers(revision, &directive_headers, validation_info)?; Ok(Self { @@ -3159,22 +3220,22 @@ impl IgvmFile { // Validate the combination of both is valid. #[cfg(debug_assertions)] { - debug_assert!(Self::validate_platform_headers( + let self_isolation_types = + Self::validate_platform_headers(self.revision, self.platform_headers.iter()) + .expect("valid file"); + let other_isolation_types = + Self::validate_platform_headers(other.revision, other.platform_headers.iter()) + .expect("valid file"); + let self_info = Self::validate_initialization_headers( self.revision, - self.platform_headers.iter() - ) - .is_ok()); - debug_assert!(Self::validate_platform_headers( - other.revision, - other.platform_headers.iter() + &self.initialization_headers, + self_isolation_types, ) - .is_ok()); - let self_info = - Self::validate_initialization_headers(self.revision, &self.initialization_headers) - .expect("valid file"); + .expect("valid file"); let other_info = Self::validate_initialization_headers( other.revision, &other.initialization_headers, + other_isolation_types, ) .expect("valid file"); debug_assert!(Self::validate_directive_headers( @@ -3329,6 +3390,9 @@ impl IgvmFile { IgvmInitializationHeader::PageTableRelocationRegion { compatibility_mask, .. } => fixup_mask(compatibility_mask), + IgvmInitializationHeader::TdInfo { + compatibility_mask, .. + } => fixup_mask(compatibility_mask), } } @@ -3996,6 +4060,36 @@ mod tests { // // test headers equivalent function + #[test] + fn test_td_info_validation() { + // Test that creating an IgvmFile with a TdInfo header but without a TDX + // platform fails. + let file = IgvmFile::new( + IgvmRevision::V1, + vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)], + vec![IgvmInitializationHeader::TdInfo { + compatibility_mask: 0x1, + xfam: 0x00000000000618e7, + }], + vec![], + ); + assert!(file.is_err()); + assert!(matches!(file.err().unwrap(), Error::InvalidPlatformType,)); + + // Test that creating an IgvmFile with a TdInfo header and a TDX + // platform succeeds. + let file = IgvmFile::new( + IgvmRevision::V1, + vec![new_platform(0x1, IgvmPlatformType::TDX)], + vec![IgvmInitializationHeader::TdInfo { + compatibility_mask: 0x1, + xfam: 0, + }], + vec![], + ); + assert!(file.is_ok()); + } + /// Test a variable header matches the supplied args. Also tests that the header deserialized returns the original /// header. fn test_variable_header( diff --git a/igvm_c/cbindgen_igvm_defs.toml b/igvm_c/cbindgen_igvm_defs.toml index c9b8bc8..6e2018d 100644 --- a/igvm_c/cbindgen_igvm_defs.toml +++ b/igvm_c/cbindgen_igvm_defs.toml @@ -7,7 +7,7 @@ language = "C" header = """ -/* +/* * SPDX-License-Identifier: MIT OR Apache-2.0 * * Copyright (c) 2023 SUSE LLC @@ -52,30 +52,31 @@ typedef uint32_t IGVM_U32_LE; """ [export] -include = ["IGVM_FIXED_HEADER", +include = ["IGVM_FIXED_HEADER", "IGVM_VHS_VARIABLE_HEADER", - "IGVM_VHS_SUPPORTED_PLATFORM", - "IGVM_VHS_GUEST_POLICY", - "GuestPolicy", - "SnpPolicy", - "TdxPolicy", - "IGVM_VHS_RELOCATABLE_REGION", - "IGVM_VHS_PAGE_TABLE_RELOCATION", - "IGVM_VHS_PARAMETER_AREA", - "IGVM_VHS_PAGE_DATA", - "IGVM_VHS_PARAMETER_INSERT", - "IGVM_VHS_PARAMETER", - "IGVM_VHS_VP_CONTEXT", - "VbsVpContextHeader", - "VbsVpContextRegister", - "IGVM_VHS_REQUIRED_MEMORY", - "IGVM_VHS_MEMORY_RANGE", - "IGVM_VHS_MMIO_RANGES", - "IGVM_VHS_SNP_ID_BLOCK_SIGNATURE", - "IGVM_VHS_SNP_ID_BLOCK_PUBLIC_KEY", - "IGVM_VHS_SNP_ID_BLOCK", - "IGVM_VHS_VBS_MEASUREMENT", - "IGVM_VHS_MEMORY_MAP_ENTRY", + "IGVM_VHS_SUPPORTED_PLATFORM", + "IGVM_VHS_GUEST_POLICY", + "GuestPolicy", + "SnpPolicy", + "TdxPolicy", + "IGVM_VHS_RELOCATABLE_REGION", + "IGVM_VHS_PAGE_TABLE_RELOCATION", + "IGVM_VHS_TD_INFO", + "IGVM_VHS_PARAMETER_AREA", + "IGVM_VHS_PAGE_DATA", + "IGVM_VHS_PARAMETER_INSERT", + "IGVM_VHS_PARAMETER", + "IGVM_VHS_VP_CONTEXT", + "VbsVpContextHeader", + "VbsVpContextRegister", + "IGVM_VHS_REQUIRED_MEMORY", + "IGVM_VHS_MEMORY_RANGE", + "IGVM_VHS_MMIO_RANGES", + "IGVM_VHS_SNP_ID_BLOCK_SIGNATURE", + "IGVM_VHS_SNP_ID_BLOCK_PUBLIC_KEY", + "IGVM_VHS_SNP_ID_BLOCK", + "IGVM_VHS_VBS_MEASUREMENT", + "IGVM_VHS_MEMORY_MAP_ENTRY", "IGVM_VHS_ERROR_RANGE"] exclude = ["IgvmPageDataFlags", "RequiredMemoryFlags", "MemoryMapEntryFlags"] diff --git a/igvm_c/sample/dump_igvm.c b/igvm_c/sample/dump_igvm.c index 3611d01..b0092f8 100644 --- a/igvm_c/sample/dump_igvm.c +++ b/igvm_c/sample/dump_igvm.c @@ -1,4 +1,4 @@ -/* +/* * SPDX-License-Identifier: MIT OR Apache-2.0 * * Copyright (c) 2023 SUSE LLC @@ -61,6 +61,8 @@ static char *igvm_type_to_text(uint32_t type) return "IGVM_VHT_RELOCATABLE_REGION"; case IGVM_VHT_PAGE_TABLE_RELOCATION_REGION: return "IGVM_VHT_PAGE_TABLE_RELOCATION_REGION"; + case IGVM_VHT_TD_INFO: + return "IGVM_VHT_TD_INFO"; case IGVM_VHT_PARAMETER_AREA: return "IGVM_VHT_PARAMETER_AREA"; case IGVM_VHT_PAGE_DATA: @@ -163,6 +165,13 @@ static void igvm_dump_variable_header(IGVM_VHS_VARIABLE_HEADER *header) printf(" UsedSize: %016lX\n", vhs->used_size); break; } + case IGVM_VHT_TD_INFO: { + IGVM_VHS_TD_INFO *vhs = + (IGVM_VHS_TD_INFO *)vh_data; + printf(" CompatibilityMask: %08X\n", vhs->compatibility_mask); + printf(" XFAM: %016lX\n", vhs->xfam); + break; + } case IGVM_VHT_PARAMETER_AREA: { IGVM_VHS_PARAMETER_AREA *vhs = (IGVM_VHS_PARAMETER_AREA *)vh_data; diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 7d11fd2..44cc882 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -248,6 +248,9 @@ pub enum IgvmVariableHeaderType { /// A page table relocation region described by /// [`IGVM_VHS_PAGE_TABLE_RELOCATION`]. IGVM_VHT_PAGE_TABLE_RELOCATION_REGION = 0x103, + /// TDX launch time configurations as described by + /// [`IGVM_VHS_TD_INFO`]. + IGVM_VHT_TD_INFO = 0x104, // These are IGVM_VHT_RANGE_DIRECTIVE structures. /// A parameter area structure described by [`IGVM_VHS_PARAMETER_AREA`]. @@ -646,6 +649,25 @@ pub struct IGVM_VHS_PAGE_TABLE_RELOCATION { pub used_size: u64, } +/// Optional launch time configurations for VMs running on TDX platform. +/// +/// The XFAM (Extended Features Allowed Mask) value specifies a mask of CPU extended features +/// that the TD is allowed to use. If the XFAM value is invalid or not supported by the host, +/// the igvm file will fail to load. If the host overrides the specified XFAM value, +/// attestation will fail. +/// +/// Only supported on TDX platforms. +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] +pub struct IGVM_VHS_TD_INFO { + /// Compatibility mask. + pub compatibility_mask: u32, + /// Reserved, must be zero. + pub reserved: u32, + /// XFAM for CPU extended features setting. + pub xfam: u64, +} + /// This structure defines a region of memory that can be used for holding /// parameters to be passed into the guest. Parameter areas are created with /// this structure, then parameters may be imported via parameter structures