Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 110 additions & 16 deletions igvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -357,6 +359,7 @@ impl IgvmInitializationHeader {
IgvmInitializationHeader::PageTableRelocationRegion { .. } => {
size_of::<IGVM_VHS_PAGE_TABLE_RELOCATION>()
}
IgvmInitializationHeader::TdInfo { .. } => size_of::<IGVM_VHS_TD_INFO>(),
};

size_of::<IGVM_VHS_VARIABLE_HEADER>() + additional
Expand All @@ -376,6 +379,7 @@ impl IgvmInitializationHeader {
IgvmInitializationHeader::PageTableRelocationRegion { .. } => {
IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION
}
IgvmInitializationHeader::TdInfo { .. } => IgvmVariableHeaderType::IGVM_VHT_TD_INFO,
}
}

Expand Down Expand Up @@ -454,6 +458,10 @@ impl IgvmInitializationHeader {

Ok(())
}
IgvmInitializationHeader::TdInfo {
compatibility_mask: _,
xfam: _,
} => Ok(()),
}
}

Expand Down Expand Up @@ -550,6 +558,22 @@ impl IgvmInitializationHeader {
vtl: vtl.try_into().map_err(|_| BinaryHeaderError::InvalidVtl)?,
}
}
IgvmVariableHeaderType::IGVM_VHT_TD_INFO if length == size_of::<IGVM_VHS_TD_INFO>() => {
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),
};
Expand All @@ -572,6 +596,9 @@ impl IgvmInitializationHeader {
PageTableRelocationRegion {
compatibility_mask, ..
} => Some(*compatibility_mask),
TdInfo {
compatibility_mask, ..
} => Some(*compatibility_mask),
}
}

Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -2348,7 +2391,7 @@ impl IgvmFile {
fn validate_platform_headers<'a>(
revision: IgvmRevision,
platform_headers: impl Iterator<Item = &'a IgvmPlatformHeader>,
) -> Result<(), Error> {
) -> Result<HashMap<IgvmPlatformType, &'a IGVM_VHS_SUPPORTED_PLATFORM>, Error> {
let mut at_least_one = false;
let mut isolation_types = HashMap::new();

Expand Down Expand Up @@ -2392,7 +2435,7 @@ impl IgvmFile {
if !at_least_one {
Err(Error::NoPlatformHeaders)
} else {
Ok(())
Ok(isolation_types)
}
}

Expand All @@ -2402,6 +2445,7 @@ impl IgvmFile {
fn validate_initialization_headers(
revision: IgvmRevision,
initialization_headers: &[IgvmInitializationHeader],
isolation_types: HashMap<IgvmPlatformType, &IGVM_VHS_SUPPORTED_PLATFORM>,
) -> Result<DirectiveHeaderValidationInfo, Error> {
let mut page_table_masks = 0;
let mut used_vp_idents: Vec<VpIdentifier> = Vec::new();
Expand Down Expand Up @@ -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
_ => {}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -3329,6 +3390,9 @@ impl IgvmFile {
IgvmInitializationHeader::PageTableRelocationRegion {
compatibility_mask, ..
} => fixup_mask(compatibility_mask),
IgvmInitializationHeader::TdInfo {
compatibility_mask, ..
} => fixup_mask(compatibility_mask),
}
}

Expand Down Expand Up @@ -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<T: IntoBytes + Immutable + KnownLayout>(
Expand Down
49 changes: 25 additions & 24 deletions igvm_c/cbindgen_igvm_defs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
language = "C"

header = """
/*
/*
* SPDX-License-Identifier: MIT OR Apache-2.0
*
* Copyright (c) 2023 SUSE LLC
Expand Down Expand Up @@ -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"]
Expand Down
11 changes: 10 additions & 1 deletion igvm_c/sample/dump_igvm.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* SPDX-License-Identifier: MIT OR Apache-2.0
*
* Copyright (c) 2023 SUSE LLC
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 22 additions & 0 deletions igvm_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really optional if it impacts the measurement of the guest? It's optional in the sense of "if you don't specify this, the VMM may choose whatever value it likes" correct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's optional in the sense that the IGVM file should load even if you don't specify these fields. I don't think all guests care if the measurements are different across different hosts, so some guests may choose not to specify these fields.

///
/// 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 host should fail to load the IGVM file. This value is reflected in attestation
/// measurement.
///
/// 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
Expand Down