diff --git a/igvm/src/c_api.rs b/igvm/src/c_api.rs index afdcdb4..e96f6a6 100644 --- a/igvm/src/c_api.rs +++ b/igvm/src/c_api.rs @@ -60,6 +60,7 @@ pub enum IgvmResult { IGVMAPI_UNSUPPORTED_PAGE_SIZE = -25, IGVMAPI_INVALID_FIXED_HEADER_ARCH = -26, IGVMAPI_MERGE_REVISION = -27, + IGVMAPI_INVALID_CCA_POLICY_COMPATIBILITY_MASK = -28, } type IgvmHandle = i32; @@ -164,6 +165,9 @@ fn translate_error(error: Error) -> IgvmResult { Error::UnsupportedPageSize(_) => IgvmResult::IGVMAPI_UNSUPPORTED_PAGE_SIZE, Error::InvalidFixedHeaderArch(_) => IgvmResult::IGVMAPI_INVALID_FIXED_HEADER_ARCH, Error::MergeRevision => IgvmResult::IGVMAPI_MERGE_REVISION, + Error::InvalidCcaPolicyCompatibilityMask(_) => { + IgvmResult::IGVMAPI_INVALID_CCA_POLICY_COMPATIBILITY_MASK + } } } diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index ebeed5f..8dcb8ad 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -64,6 +64,8 @@ pub enum IsolationType { Sev, /// This guest is isolated with SEV-ES (physical or emulated). SevEs, + /// This guest is isolated with Cca (physical or emulated). + Cca, } impl From for igvm_defs::IgvmPlatformType { @@ -75,6 +77,7 @@ impl From for igvm_defs::IgvmPlatformType { IsolationType::Tdx => IgvmPlatformType::TDX, IsolationType::Sev => IgvmPlatformType::SEV, IsolationType::SevEs => IgvmPlatformType::SEV_ES, + IsolationType::Cca => IgvmPlatformType::CCA, } } } @@ -262,6 +265,11 @@ impl IgvmPlatformHeader { } // TODO: shared gpa boundary req? } + IgvmPlatformType::CCA => { + if info.platform_version != IGVM_CCA_PLATFORM_VERSION { + return Err(BinaryHeaderError::InvalidPlatformVersion); + } + } _ => { return Err(BinaryHeaderError::InvalidPlatformType); } @@ -322,6 +330,13 @@ pub enum IgvmInitializationHeader { policy: u64, compatibility_mask: u32, }, + CcaPolicy { + compatibility_mask: u32, + attributes_required_zeroes: u64, + attributes_required_ones: u64, + hash_algorithm: CcaHashAlgorithm, + lfa_policy: CcaLfaPolicy, + }, RelocatableRegion { compatibility_mask: u32, relocation_alignment: u64, @@ -366,6 +381,7 @@ impl IgvmInitializationHeader { fn header_size(&self) -> usize { let additional = match self { IgvmInitializationHeader::GuestPolicy { .. } => size_of::(), + IgvmInitializationHeader::CcaPolicy { .. } => size_of::(), IgvmInitializationHeader::RelocatableRegion { .. } => { size_of::() } @@ -389,6 +405,9 @@ impl IgvmInitializationHeader { IgvmInitializationHeader::GuestPolicy { .. } => { IgvmVariableHeaderType::IGVM_VHT_GUEST_POLICY } + IgvmInitializationHeader::CcaPolicy { .. } => { + IgvmVariableHeaderType::IGVM_VHT_CCA_POLICY + } IgvmInitializationHeader::RelocatableRegion { .. } => { IgvmVariableHeaderType::IGVM_VHT_RELOCATABLE_REGION } @@ -408,6 +427,18 @@ impl IgvmInitializationHeader { /// Checks if this header contains valid state. fn validate(&self) -> Result<(), BinaryHeaderError> { + // Checker for the ones and zeroes bitmasks which are used by a few architectures. + fn validate_ones_zeroes_conflict( + required_zeroes: u64, + required_ones: u64, + ) -> Result<(), BinaryHeaderError> { + if required_zeroes & required_ones != 0 { + return Err(BinaryHeaderError::InvalidOnesZeroesMasks); + } + + Ok(()) + } + match self { IgvmInitializationHeader::GuestPolicy { policy: _, @@ -416,6 +447,20 @@ impl IgvmInitializationHeader { // TODO: check policy bits? Ok(()) } + IgvmInitializationHeader::CcaPolicy { + compatibility_mask: _, + attributes_required_zeroes, + attributes_required_ones, + hash_algorithm: _, + lfa_policy: _, + } => { + validate_ones_zeroes_conflict( + *attributes_required_zeroes, + *attributes_required_ones, + )?; + + Ok(()) + } IgvmInitializationHeader::RelocatableRegion { compatibility_mask: _, relocation_alignment, @@ -547,6 +592,31 @@ impl IgvmInitializationHeader { compatibility_mask, } } + IgvmVariableHeaderType::IGVM_VHT_CCA_POLICY + if length == size_of::() => + { + let IGVM_VHS_CCA_POLICY { + compatibility_mask, + reserved, + attributes_required_zeroes, + attributes_required_ones, + hash_algorithm, + lfa_policy, + reserved2, + } = read_header(&mut variable_headers)?; + + if reserved != 0 || reserved2 != [0; 6] { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmInitializationHeader::CcaPolicy { + compatibility_mask, + attributes_required_zeroes, + attributes_required_ones, + hash_algorithm, + lfa_policy, + } + } IgvmVariableHeaderType::IGVM_VHT_RELOCATABLE_REGION if length == size_of::() => { @@ -665,6 +735,9 @@ impl IgvmInitializationHeader { GuestPolicy { compatibility_mask, .. } => Some(*compatibility_mask), + CcaPolicy { + compatibility_mask, .. + } => Some(*compatibility_mask), RelocatableRegion { compatibility_mask, .. } => Some(*compatibility_mask), @@ -710,6 +783,29 @@ impl IgvmInitializationHeader { variable_headers, ); } + IgvmInitializationHeader::CcaPolicy { + compatibility_mask, + attributes_required_zeroes, + attributes_required_ones, + hash_algorithm, + lfa_policy, + } => { + let info = IGVM_VHS_CCA_POLICY { + compatibility_mask: *compatibility_mask, + reserved: 0, + attributes_required_zeroes: *attributes_required_zeroes, + attributes_required_ones: *attributes_required_ones, + hash_algorithm: *hash_algorithm, + lfa_policy: *lfa_policy, + reserved2: [0; 6], + }; + + append_header( + &info, + IgvmVariableHeaderType::IGVM_VHT_CCA_POLICY, + variable_headers, + ); + } IgvmInitializationHeader::RelocatableRegion { compatibility_mask, relocation_alignment, @@ -883,6 +979,11 @@ pub enum IgvmDirectiveHeader { registers: Vec, compatibility_mask: u32, }, + AArch64CcaVpContext { + compatibility_mask: u32, + vp_index: u16, + context: Box, + }, ParameterInsert(IGVM_VHS_PARAMETER_INSERT), ErrorRange { gpa: u64, @@ -1089,6 +1190,8 @@ pub enum BinaryHeaderError { InvalidPlatformVersion, #[error("invalid shared gpa boundary")] InvalidSharedGpaBoundary, + #[error("invalid ones and zeroes masks")] + InvalidOnesZeroesMasks, #[error("reserved values not zero")] ReservedNotZero, #[error("VBS vp context has no registers")] @@ -1137,6 +1240,7 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::X64NativeVpContext { .. } => size_of::(), IgvmDirectiveHeader::X64VbsVpContext { .. } => size_of::(), IgvmDirectiveHeader::AArch64VbsVpContext { .. } => size_of::(), + IgvmDirectiveHeader::AArch64CcaVpContext { .. } => size_of::(), IgvmDirectiveHeader::ParameterInsert(param) => size_of_val(param), IgvmDirectiveHeader::ErrorRange { .. } => size_of::(), IgvmDirectiveHeader::SnpIdBlock { .. } => size_of::(), @@ -1177,6 +1281,9 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::AArch64VbsVpContext { .. } => { IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT } + IgvmDirectiveHeader::AArch64CcaVpContext { .. } => { + IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT + } IgvmDirectiveHeader::ParameterInsert(_) => { IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT } @@ -1432,6 +1539,36 @@ impl IgvmDirectiveHeader { variable_headers, ); } + IgvmDirectiveHeader::AArch64CcaVpContext { + compatibility_mask, + vp_index, + context, + } => { + // Pad file data to 4K. + let align_up_iter = + std::iter::repeat_n(&0u8, PAGE_SIZE_4K as usize - context.as_bytes().len()); + let data: Vec = context + .as_bytes() + .iter() + .chain(align_up_iter) + .copied() + .collect(); + let file_offset = file_data.write_file_data(&data); + + let info = IGVM_VHS_VP_CONTEXT { + gpa: 0.into(), + compatibility_mask: *compatibility_mask, + file_offset, + vp_index: *vp_index, + reserved: 0, + }; + + append_header( + &info, + IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT, + variable_headers, + ); + } IgvmDirectiveHeader::X64VbsVpContext { vtl, registers, @@ -1627,6 +1764,9 @@ impl IgvmDirectiveHeader { AArch64VbsVpContext { compatibility_mask, .. } => Some(*compatibility_mask), + AArch64CcaVpContext { + compatibility_mask, .. + } => Some(*compatibility_mask), ParameterInsert(info) => Some(info.compatibility_mask), ErrorRange { compatibility_mask, .. @@ -1675,6 +1815,9 @@ impl IgvmDirectiveHeader { AArch64VbsVpContext { compatibility_mask, .. } => Some(compatibility_mask), + AArch64CcaVpContext { + compatibility_mask, .. + } => Some(compatibility_mask), ParameterInsert(info) => Some(&mut info.compatibility_mask), ErrorRange { compatibility_mask, .. @@ -1831,6 +1974,11 @@ impl IgvmDirectiveHeader { registers: _, compatibility_mask: _, } => {} + IgvmDirectiveHeader::AArch64CcaVpContext { + compatibility_mask: _, + vp_index: _, + context: _, + } => {} IgvmDirectiveHeader::ParameterInsert(param) => { if param.gpa % PAGE_SIZE_4K != 0 { return Err(BinaryHeaderError::UnalignedAddress(param.gpa)); @@ -2068,6 +2216,35 @@ impl IgvmDirectiveHeader { context, } } + Some(IgvmPlatformType::CCA) => { + // Read the context which is stored as 4K file data. + let start = (header.file_offset - file_data_start) as usize; + if file_data.len() < start { + return Err(BinaryHeaderError::InvalidDataSize); + } + + let data = file_data + .get(start..) + .and_then(|x| x.get(..PAGE_SIZE_4K as usize)) + .ok_or(BinaryHeaderError::InvalidDataSize)?; + + // Copy the context bytes into the context structure, + // and validate the remaining bytes are 0. + // todo: zerocopy: as of 0.8, can recover from allocation failure + let mut context = IgvmVpContextAArch64Cca::new_box_zeroed().unwrap(); + let (context_slice, remaining) = + data.split_at(size_of::()); + context.as_mut_bytes().copy_from_slice(context_slice); + if remaining.iter().any(|b| *b != 0) { + return Err(BinaryHeaderError::InvalidContext); + } + + IgvmDirectiveHeader::AArch64CcaVpContext { + compatibility_mask: header.compatibility_mask, + vp_index: header.vp_index, + context, + } + } _ => { // Unsupported compatibility mask or isolation type return Err(BinaryHeaderError::InvalidVpContextPlatformType); @@ -2258,6 +2435,8 @@ pub enum Error { InvalidBinaryVariableHeaderSection, #[error("invalid checksum in fixed header, expected {expected} was {header_value}")] InvalidChecksum { expected: u32, header_value: u32 }, + #[error("Cca policy compatibility mask 0x{0:x} does not reference a Cca platform")] + InvalidCcaPolicyCompatibilityMask(u32), #[error("page table relocation header specified twice for a compatibiltiy mask")] MultiplePageTableRelocationHeaders, #[error("relocation regions overlap")] @@ -2537,6 +2716,14 @@ impl IgvmFile { }); } } + IgvmPlatformType::CCA => { + if revision.arch() != Arch::AArch64 { + return Err(Error::PlatformArchUnsupported { + arch: revision.arch(), + platform: info.platform_type, + }); + } + } _ => return Err(Error::InvalidPlatformType), } @@ -2564,12 +2751,21 @@ impl IgvmFile { /// Returns additional info used to validate directive headers. fn validate_initialization_headers( revision: IgvmRevision, + platform_headers: &[IgvmPlatformHeader], initialization_headers: &[IgvmInitializationHeader], ) -> Result { let mut page_table_masks = 0; let mut used_vp_idents: Vec = Vec::new(); let mut reloc_regions: HashMap> = HashMap::new(); let mut page_table_regions = Vec::new(); + let platform_mask_map: HashMap = platform_headers + .iter() + .map(|header| match header { + IgvmPlatformHeader::SupportedPlatform(info) => { + (info.compatibility_mask, info.platform_type) + } + }) + .collect(); let mut check_region_overlap = |compatibility_mask: u32, start: u64, size: u64| -> Result<(), Error> { @@ -2614,6 +2810,22 @@ impl IgvmFile { // - Keep track of which page table regions, vp_index, and vtls for // use in validating directive headers. match header { + IgvmInitializationHeader::CcaPolicy { + compatibility_mask, .. + } => { + if !matches!(revision.arch(), Arch::AArch64) { + return Err(Error::InvalidHeaderArch { + arch: revision.arch(), + header_type: "CcaPolicy".into(), + }); + } + + for mask in extract_individual_masks(*compatibility_mask) { + if platform_mask_map.get(&mask) != Some(&IgvmPlatformType::CCA) { + return Err(Error::InvalidCcaPolicyCompatibilityMask(mask)); + } + } + } IgvmInitializationHeader::PageTableRelocationRegion { compatibility_mask, gpa, @@ -2843,6 +3055,18 @@ impl IgvmFile { && ident.vtl == *vtl) }) } + IgvmDirectiveHeader::AArch64CcaVpContext { + compatibility_mask: _, + context: _, + vp_index: _, + } => { + if revision.arch() != Arch::AArch64 { + return Err(Error::InvalidHeaderArch { + arch: revision.arch(), + header_type: "AArch64CcaVpContext".into(), + }); + } + } IgvmDirectiveHeader::ParameterInsert(info) => { match parameter_areas.get_mut(&info.parameter_area_index) { Some(state) if *state == ParameterAreaState::Allocated => { @@ -3007,8 +3231,11 @@ impl IgvmFile { } Self::validate_platform_headers(revision, platform_headers.iter())?; - let validation_info = - Self::validate_initialization_headers(revision, &initialization_headers)?; + let validation_info = Self::validate_initialization_headers( + revision, + &platform_headers, + &initialization_headers, + )?; Self::validate_directive_headers(revision, &directive_headers, validation_info)?; Ok(Self { @@ -3386,11 +3613,15 @@ impl IgvmFile { other.platform_headers.iter() ) .is_ok()); - let self_info = - Self::validate_initialization_headers(self.revision, &self.initialization_headers) - .expect("valid file"); + let self_info = Self::validate_initialization_headers( + self.revision, + &self.platform_headers, + &self.initialization_headers, + ) + .expect("valid file"); let other_info = Self::validate_initialization_headers( other.revision, + &other.platform_headers, &other.initialization_headers, ) .expect("valid file"); @@ -3540,6 +3771,9 @@ impl IgvmFile { policy: _, compatibility_mask, } => fixup_mask(compatibility_mask), + IgvmInitializationHeader::CcaPolicy { + compatibility_mask, .. + } => fixup_mask(compatibility_mask), IgvmInitializationHeader::RelocatableRegion { compatibility_mask, .. } => fixup_mask(compatibility_mask), @@ -3575,7 +3809,8 @@ impl IgvmFile { | SnpIdBlock { .. } | VbsMeasurement { .. } | X64VbsVpContext { .. } - | AArch64VbsVpContext { .. } => {} + | AArch64VbsVpContext { .. } + | AArch64CcaVpContext { .. } => {} ParameterArea { parameter_area_index, .. @@ -3818,7 +4053,7 @@ mod tests { } #[test] - fn test_basic_v2_aarch64() { + fn test_basic_v2_aarch64_vbs() { let data1 = vec![1; PAGE_SIZE_4K as usize]; let data2 = vec![2; PAGE_SIZE_4K as usize]; let data3 = vec![3; PAGE_SIZE_4K as usize]; @@ -3856,6 +4091,52 @@ mod tests { assert_igvm_equal(&file, &deserialized_binary_file); } + #[test] + fn test_basic_v2_aarch64_cca() { + let data1 = vec![1; PAGE_SIZE_4K as usize]; + let data2 = vec![2; PAGE_SIZE_4K as usize]; + let data3 = vec![3; PAGE_SIZE_4K as usize]; + let data4 = vec![4; PAGE_SIZE_4K as usize]; + let context = IgvmVpContextAArch64Cca::new_box_zeroed().unwrap(); + let file = IgvmFile { + revision: IgvmRevision::V2 { + arch: Arch::AArch64, + page_size: PAGE_SIZE_4K as u32, + }, + platform_headers: vec![new_platform(0x1, IgvmPlatformType::CCA)], + initialization_headers: vec![IgvmInitializationHeader::CcaPolicy { + compatibility_mask: 0x1, + attributes_required_zeroes: CCA_POLICY_ATTR_MEC, + attributes_required_ones: CCA_POLICY_ATTR_DEBUG, + hash_algorithm: CcaHashAlgorithm::SHA512, + lfa_policy: CcaLfaPolicy::LFA_ALLOW, + }], + directive_headers: vec![ + new_page_data(0, 1, &data1), + new_page_data(1, 1, &data2), + new_page_data(2, 1, &data3), + new_page_data(4, 1, &data4), + new_page_data(10, 1, &data1), + new_page_data(11, 1, &data2), + new_page_data(12, 1, &data3), + new_page_data(14, 1, &data4), + new_parameter_area(0), + new_parameter_usage(0), + new_parameter_insert(20, 0, 1), + IgvmDirectiveHeader::AArch64CcaVpContext { + compatibility_mask: 0x1, + vp_index: 0xabcd, + context, + }, + ], + }; + let mut binary_file = Vec::new(); + file.serialize(&mut binary_file).unwrap(); + + let deserialized_binary_file = IgvmFile::new_from_binary(&binary_file, None).unwrap(); + assert_igvm_equal(&file, &deserialized_binary_file); + } + // test platform filter works correctly // test state transition checks enforce correct ordering } @@ -4336,6 +4617,7 @@ mod tests { /// Test an initialization variable header matches the supplied args. Also /// tests round-trip serialization/deserialization. + #[cfg(feature = "corim")] fn test_init_variable_header( header: IgvmInitializationHeader, file_data_offset: u32, @@ -4634,6 +4916,65 @@ mod tests { ); } + #[test] + fn test_cca_policy_reserved_must_be_zero() { + let assert_reserved_not_zero = |raw_header: IGVM_VHS_CCA_POLICY| { + let mut binary_header = Vec::new(); + append_header( + &raw_header, + IgvmVariableHeaderType::IGVM_VHT_CCA_POLICY, + &mut binary_header, + ); + + assert!(matches!( + IgvmInitializationHeader::new_from_binary_split(&binary_header, &[], 0), + Err(BinaryHeaderError::ReservedNotZero) + )); + }; + + let raw_header = IGVM_VHS_CCA_POLICY { + compatibility_mask: 0x1, + reserved: 1, + attributes_required_zeroes: 0, + attributes_required_ones: 0, + hash_algorithm: CcaHashAlgorithm::SHA256, + lfa_policy: CcaLfaPolicy::LFA_DISALLOW, + reserved2: [0; 6], + }; + assert_reserved_not_zero(raw_header); + + let raw_header = IGVM_VHS_CCA_POLICY { + reserved: 0, + reserved2: [1, 0, 0, 0, 0, 0], + ..raw_header + }; + assert_reserved_not_zero(raw_header); + } + + #[test] + fn test_cca_policy_requires_cca_platform() { + let file = IgvmFile::new( + IgvmRevision::V2 { + arch: Arch::AArch64, + page_size: PAGE_SIZE_4K as u32, + }, + vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)], + vec![IgvmInitializationHeader::CcaPolicy { + compatibility_mask: 0x1, + attributes_required_zeroes: 0, + attributes_required_ones: 0, + hash_algorithm: CcaHashAlgorithm::SHA512, + lfa_policy: CcaLfaPolicy::LFA_ALLOW, + }], + vec![], + ); + + assert!(matches!( + file, + Err(Error::InvalidCcaPolicyCompatibilityMask(0x1)) + )); + } + #[test] fn test_parameter_area_bad_size() { // Non page size number of bytes @@ -5025,11 +5366,13 @@ mod tests { // updated to have real documents. fn validate(headers: &[IgvmInitializationHeader]) -> Result<(), Error> { + let platform_headers = [new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)]; IgvmFile::validate_initialization_headers( IgvmRevision::V2 { arch: Arch::X64, page_size: PAGE_SIZE_4K as u32, }, + &platform_headers, headers, ) .map(|_| ()) diff --git a/igvm/src/registers.rs b/igvm/src/registers.rs index ea247a7..129bc0b 100644 --- a/igvm/src/registers.rs +++ b/igvm/src/registers.rs @@ -178,11 +178,18 @@ impl X86Register { } /// AArch64 registers that can be stored in IGVM VP context structures. +/// The list can be extended if new registers are needed by new context. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AArch64Register { Pc(u64), X0(u64), X1(u64), + X2(u64), + X3(u64), + X4(u64), + X5(u64), + X6(u64), + X7(u64), Cpsr(u64), SctlrEl1(u64), TcrEl1(u64), @@ -198,6 +205,12 @@ impl AArch64Register { AArch64Register::Pc(reg) => (HvArm64RegisterName::XPc, reg.into()), AArch64Register::X0(reg) => (HvArm64RegisterName::X0, reg.into()), AArch64Register::X1(reg) => (HvArm64RegisterName::X1, reg.into()), + AArch64Register::X2(reg) => (HvArm64RegisterName::X2, reg.into()), + AArch64Register::X3(reg) => (HvArm64RegisterName::X3, reg.into()), + AArch64Register::X4(reg) => (HvArm64RegisterName::X4, reg.into()), + AArch64Register::X5(reg) => (HvArm64RegisterName::X5, reg.into()), + AArch64Register::X6(reg) => (HvArm64RegisterName::X6, reg.into()), + AArch64Register::X7(reg) => (HvArm64RegisterName::X7, reg.into()), AArch64Register::Cpsr(reg) => (HvArm64RegisterName::Cpsr, reg.into()), AArch64Register::SctlrEl1(reg) => (HvArm64RegisterName::SctlrEl1, reg.into()), AArch64Register::TcrEl1(reg) => (HvArm64RegisterName::TcrEl1, reg.into()), @@ -287,6 +300,12 @@ impl TryFrom for AArch64Register { HvArm64RegisterName::XPc => Self::Pc(register_value.as_u64()), HvArm64RegisterName::X0 => Self::X0(register_value.as_u64()), HvArm64RegisterName::X1 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X2 => Self::X2(register_value.as_u64()), + HvArm64RegisterName::X3 => Self::X3(register_value.as_u64()), + HvArm64RegisterName::X4 => Self::X4(register_value.as_u64()), + HvArm64RegisterName::X5 => Self::X5(register_value.as_u64()), + HvArm64RegisterName::X6 => Self::X6(register_value.as_u64()), + HvArm64RegisterName::X7 => Self::X7(register_value.as_u64()), HvArm64RegisterName::Cpsr => Self::Cpsr(register_value.as_u64()), HvArm64RegisterName::SctlrEl1 => Self::SctlrEl1(register_value.as_u64()), HvArm64RegisterName::TcrEl1 => Self::TcrEl1(register_value.as_u64()), diff --git a/igvm_c/cbindgen_igvm_defs.toml b/igvm_c/cbindgen_igvm_defs.toml index c9b8bc8..1a08efa 100644 --- a/igvm_c/cbindgen_igvm_defs.toml +++ b/igvm_c/cbindgen_igvm_defs.toml @@ -56,6 +56,9 @@ include = ["IGVM_FIXED_HEADER", "IGVM_VHS_VARIABLE_HEADER", "IGVM_VHS_SUPPORTED_PLATFORM", "IGVM_VHS_GUEST_POLICY", + "IGVM_VHS_CCA_POLICY", + "CCA_POLICY_ATTR_DEBUG", + "CCA_POLICY_ATTR_MEC", "GuestPolicy", "SnpPolicy", "TdxPolicy", @@ -88,4 +91,4 @@ exclude = ["IgvmPageDataFlags", "RequiredMemoryFlags", "MemoryMapEntryFlags"] "VbsVpContextRegister" = "IgvmVbsVpContextRegister" "PAGE_SIZE_4K" = "IGVM_PAGE_SIZE_4K" "u64_le" = "IGVM_U64_LE" -"u32_le" = "IGVM_U32_LE" \ No newline at end of file +"u32_le" = "IGVM_U32_LE" diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 56210fa..d649746 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -241,6 +241,10 @@ pub enum IgvmVariableHeaderType { // These are IGVM_VHT_RANGE_INIT structures. /// The isolation architecture policy for the guest, as defined by /// [`IGVM_VHS_GUEST_POLICY`]. + /// NOTE: This header is maintained for backwards compatability with older + /// IGVM files, but newer files should use the individual policy + /// header type and structure for each architecture. For example, CCA + /// has defined IGVM_VHT_CCA_POLICY and IGVM_VHS_CCA_POLICY. IGVM_VHT_GUEST_POLICY = 0x101, /// A relocatable region structure described by /// [`IGVM_VHS_RELOCATABLE_REGION`]. @@ -283,6 +287,8 @@ pub enum IgvmVariableHeaderType { #[cfg(feature = "corim")] #[cfg_attr(docsrs, doc(cfg(feature = "corim")))] IGVM_VHT_CORIM_SIGNATURE = 0x105, + /// A CCA policy structure described by [`IGVM_VHS_CCA_POLICY`]. + IGVM_VHT_CCA_POLICY = 0x106, // These are IGVM_VHT_RANGE_DIRECTIVE structures. /// A parameter area structure described by [`IGVM_VHS_PARAMETER_AREA`]. @@ -421,6 +427,10 @@ pub enum IgvmPlatformType { #[cfg(feature = "unstable")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] SEV_ES = 0x05, + /// Arm CCA + #[cfg(feature = "unstable")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + CCA = 0x06, } impl Default for IgvmPlatformType { @@ -445,6 +455,10 @@ pub const IGVM_SEV_PLATFORM_VERSION: u16 = 0x1; #[cfg(feature = "unstable")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] pub const IGVM_SEV_ES_PLATFORM_VERSION: u16 = 0x1; +/// Platform version for [`IgvmPlatformType::Cca`]. +#[cfg(feature = "unstable")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] +pub const IGVM_CCA_PLATFORM_VERSION: u16 = 0x1; /// This structure indicates which isolation platforms are compatible with this /// guest image. A separate [`IGVM_VHS_SUPPORTED_PLATFORM`] structure must be @@ -533,6 +547,61 @@ pub struct TdxPolicy { pub reserved: u64, } +/// Hash algorithms for Arm CCA used in [`CcaPolicy::hash_algorithm`]. +/// These algorithms correspond to those defined in the RMM specification, +/// section "C2.24 RmmHashAlgorithm type". +#[open_enum] +#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum CcaHashAlgorithm { + /// SHA-256 hash algorithm + SHA256 = 0x0, + /// SHA-512 hash algorithm + SHA512 = 0x1, + /// SHA-384 hash algorithm + SHA384 = 0x2, +} + +/// Live Firmware Activation (LFA) policies for Arm CCA used in [`CcaPolicy::lfa_policy`]. +#[open_enum] +#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum CcaLfaPolicy { + /// Components within the Realm's TCB cannot be updated via LFA while + /// the realm is running. + LFA_DISALLOW = 0x0, + /// Components within the Realm's TCB can be updated via LFA while + /// the realm is running. + LFA_ALLOW = 0x1, +} + +/// Whether debug is allowed for the Realm. +pub const CCA_POLICY_ATTR_DEBUG: u64 = 1 << 0; +/// Whether the Memory Encryption Context (MEC) is shared. +pub const CCA_POLICY_ATTR_MEC: u64 = 1 << 1; +/// The Arm CCA policy used in [`IgvmVariableHeaderType::IGVM_VHT_CCA_POLICY`]. +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] +pub struct IGVM_VHS_CCA_POLICY { + /// Compatibility mask. + pub compatibility_mask: u32, + /// Reserved, must be zero. + pub reserved: u32, + /// - `required_zeros = 1`, `required_ones = 0`: the bit must be clear. + /// - `required_zeros = 0`, `required_ones = 1`: the bit must be set. + /// - `required_zeros = 0`, `required_ones = 0`: the bit is don't-care. + /// - `required_zeros = 1`, `required_ones = 1`: invalid (conflict) + pub attributes_required_zeroes: u64, + /// Required ones for attributes. + pub attributes_required_ones: u64, + /// Hash algorithm to measure the initial state of the Realm. + pub hash_algorithm: CcaHashAlgorithm, + /// Live Firmware Activation (LFA) policy for the components within the Realm's TCB. + pub lfa_policy: CcaLfaPolicy, + /// Reserved, must be zero. + pub reserved2: [u8; 6], +} + /// This region describes VTL2. pub const IGVM_VHF_RELOCATABLE_REGION_IS_VTL2: u8 = 0x1; /// The starting executable address for the specified VP and VTL should be @@ -1011,6 +1080,9 @@ pub struct IgvmNativeVpContextX64 { /// /// The format consists of a [`VbsVpContextHeader`] followed by a /// `register_count` of [`VbsVpContextRegister`]. +/// +/// NOTE: we reuse [`VbsVpContextHeader`] and [`VbsVpContextHeader`] for ARM64 +/// CCA and `vtl` means `plane` when used for CCA. #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] pub struct VbsVpContextHeader { @@ -1035,6 +1107,47 @@ pub struct VbsVpContextRegister { const_assert_eq!(size_of::(), 0x20); +/// Format of [`IGVM_VHS_VP_CONTEXT`] file data for a native ARM64 CCA image. +/// +/// The VP Context corresponds to the REC (Realm Execution Context) in CCA. +/// Therefore, the fields listed below match those defined in the RMM +/// specification, section "B4.6.69 RmiRecParams type". +/// +/// These include the general-purpose registers x0–x7, the PC register, +/// and a 64-bit flags field. All of these fields contribute to the +/// RIM (Realm Initial Measurement). +/// +/// One exception is `mpidr`, which is not included here because it is +/// determined by the host and does not need to be specified in the +/// IGVM file. +/// +/// Any registers not explicitly specified here are initialized to their +/// architectural reset values. +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] +pub struct IgvmVpContextAArch64Cca { + /// X0 register. + pub x0: u64, + /// X1 register. + pub x1: u64, + /// X2 register. + pub x2: u64, + /// X3 register. + pub x3: u64, + /// X4 register. + pub x4: u64, + /// X5 register. + pub x5: u64, + /// X6 register. + pub x6: u64, + /// X7 register. + pub x7: u64, + /// Program counter. + pub pc: u64, + /// Flags for the context. + pub flags: u64, +} + /// This structure describes memory the IGVM file expects to be present in the /// guest. This is a hint to the loader that the guest will not function without /// memory present at the specified range, and should terminate the load process