From 51147d8bd203d312703fd45df92cc92380ab5dee Mon Sep 17 00:00:00 2001 From: "Jiong Wang (Arm Ltd)" Date: Fri, 7 Nov 2025 11:28:04 +0000 Subject: [PATCH 1/4] arm64: extend the specification for CCA and update parser lib This PR extends 'igvm_def' and 'igvm' crates to support ARM64 CCA. igvm_def: - New IgvmPlatformType::CCA and associated IGVM_CCA_PLATFORM_VERSION. - New CcaPolicy to be used with IGVM_VHS_GUEST_POLICY. - CCA VpContext follows the convention used by VbsVPContext: a fixed header followed by a list of registers, this gives flexibility on configuring registers' initial value. igvm: - updated a few places to recognize the new CCA platform Signed-off-by: Ionut Mihalcea Signed-off-by: Jiong Wang --- igvm/src/lib.rs | 65 ++++++++++++++++++++++++++++++- igvm/src/registers.rs | 90 +++++++++++++++++++++++++++++++++++++++++++ igvm_defs/src/lib.rs | 57 +++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index 9a52865..e0afff2 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); } @@ -1783,7 +1791,7 @@ impl IgvmDirectiveHeader { .ok_or(BinaryHeaderError::InvalidVariableHeaderSize)?; match compatibility_mask_to_platforms(header.compatibility_mask) { - Some(IgvmPlatformType::VSM_ISOLATION) => { + Some(IgvmPlatformType::VSM_ISOLATION) | Some(IgvmPlatformType::CCA) => { // First read the VbsVpContextHeader at file offset let start = (header.file_offset - file_data_start) as usize; let (VbsVpContextHeader { register_count }, remaining_data) = @@ -2374,6 +2382,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), } @@ -3463,6 +3479,13 @@ mod tests { }) } + fn new_guest_policy(policy: u64, compatibility_mask: u32) -> IgvmInitializationHeader { + IgvmInitializationHeader::GuestPolicy { + policy, + compatibility_mask, + } + } + fn new_page_data(page: u64, compatibility_mask: u32, data: &[u8]) -> IgvmDirectiveHeader { IgvmDirectiveHeader::PageData { gpa: page * PAGE_SIZE_4K, @@ -3631,6 +3654,46 @@ 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 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![new_guest_policy((CcaHashAlgorithm::SHA512.0 as u64) << 1 + | (CcaLfaPolicy::LFA_ALLOW.0 as u64) << 9, 0x1)], + 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::AArch64VbsVpContext { + vtl: Vtl::Vtl0, + registers: vec![AArch64Register::X0(0x1234)], + compatibility_mask: 0x1, + }, + ], + }; + 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 } diff --git a/igvm/src/registers.rs b/igvm/src/registers.rs index ea247a7..48b69a8 100644 --- a/igvm/src/registers.rs +++ b/igvm/src/registers.rs @@ -183,6 +183,36 @@ pub enum AArch64Register { Pc(u64), X0(u64), X1(u64), + X2(u64), + X3(u64), + X4(u64), + X5(u64), + X6(u64), + X7(u64), + X8(u64), + X9(u64), + X10(u64), + X11(u64), + X12(u64), + X13(u64), + X14(u64), + X15(u64), + X16(u64), + X17(u64), + X18(u64), + X19(u64), + X20(u64), + X21(u64), + X22(u64), + X23(u64), + X24(u64), + X25(u64), + X26(u64), + X27(u64), + X28(u64), + XFp(u64), + XLr(u64), + XSp(u64), Cpsr(u64), SctlrEl1(u64), TcrEl1(u64), @@ -198,6 +228,36 @@ 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::X8(reg) => (HvArm64RegisterName::X8, reg.into()), + AArch64Register::X9(reg) => (HvArm64RegisterName::X9, reg.into()), + AArch64Register::X10(reg) => (HvArm64RegisterName::X10, reg.into()), + AArch64Register::X11(reg) => (HvArm64RegisterName::X11, reg.into()), + AArch64Register::X12(reg) => (HvArm64RegisterName::X12, reg.into()), + AArch64Register::X13(reg) => (HvArm64RegisterName::X13, reg.into()), + AArch64Register::X14(reg) => (HvArm64RegisterName::X14, reg.into()), + AArch64Register::X15(reg) => (HvArm64RegisterName::X15, reg.into()), + AArch64Register::X16(reg) => (HvArm64RegisterName::X16, reg.into()), + AArch64Register::X17(reg) => (HvArm64RegisterName::X17, reg.into()), + AArch64Register::X18(reg) => (HvArm64RegisterName::X18, reg.into()), + AArch64Register::X19(reg) => (HvArm64RegisterName::X19, reg.into()), + AArch64Register::X20(reg) => (HvArm64RegisterName::X20, reg.into()), + AArch64Register::X21(reg) => (HvArm64RegisterName::X21, reg.into()), + AArch64Register::X22(reg) => (HvArm64RegisterName::X22, reg.into()), + AArch64Register::X23(reg) => (HvArm64RegisterName::X23, reg.into()), + AArch64Register::X24(reg) => (HvArm64RegisterName::X24, reg.into()), + AArch64Register::X25(reg) => (HvArm64RegisterName::X25, reg.into()), + AArch64Register::X26(reg) => (HvArm64RegisterName::X26, reg.into()), + AArch64Register::X27(reg) => (HvArm64RegisterName::X27, reg.into()), + AArch64Register::X28(reg) => (HvArm64RegisterName::X28, reg.into()), + AArch64Register::XFp(reg) => (HvArm64RegisterName::XFp, reg.into()), + AArch64Register::XLr(reg) => (HvArm64RegisterName::XLr, reg.into()), + AArch64Register::XSp(reg) => (HvArm64RegisterName::XSp, 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 +347,36 @@ 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::X1(register_value.as_u64()), + HvArm64RegisterName::X3 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X4 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X5 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X6 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X7 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X8 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X9 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X10 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X11 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X12 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X13 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X14 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X15 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X16 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X17 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X18 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X19 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X20 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X21 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X22 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X23 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X24 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X25 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X26 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X27 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::X28 => Self::X1(register_value.as_u64()), + HvArm64RegisterName::XFp => Self::X1(register_value.as_u64()), + HvArm64RegisterName::XLr => Self::X1(register_value.as_u64()), + HvArm64RegisterName::XSp => Self::X1(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_defs/src/lib.rs b/igvm_defs/src/lib.rs index 7d11fd2..3dadf79 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -386,6 +386,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 { @@ -410,6 +414,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 @@ -451,6 +459,8 @@ pub struct IGVM_VHS_GUEST_POLICY { /// For AMD SEV-SNP, this is [`SnpPolicy`]. /// /// For Intel TDX, this is [`TdxPolicy`]. + /// + /// For Arm CCA, this is [`CcaPolicy`]. pub policy: u64, /// Compatibility mask. pub compatibility_mask: u32, @@ -498,6 +508,50 @@ pub struct TdxPolicy { pub reserved: u64, } +/// The Arm CCA policy used in [`IGVM_VHS_GUEST_POLICY::policy`]. +#[bitfield(u64)] +#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] +pub struct CcaPolicy { + /// Whether debug is allowed for the Realm. + #[bits(1)] + pub debug_allowed: u8, + /// Hash algorithm to measure the initial state of the Realm. + #[bits(8)] + pub hash_algorithm: u8, + /// Live Firmware Activation (LFA) policy for the components within the Realm's TCB. + #[bits(2)] + pub lfa_policy: u8, + /// Whether the Memory Encryption Context (MEC) is shared. + #[bits(1)] + pub mec_shared: u8, + #[bits(52)] + pub reserved: u64, +} + +/// Hash algorithms for Arm CCA used in [`CcaPolicy::hash_algorithm`]. +#[open_enum] +#[repr(u8)] +pub enum CcaHashAlgorithm { + /// SHA-256 hash algorithm + SHA256 = 0x0, + /// SHA-384 hash algorithm + SHA384 = 0x1, + /// SHA-512 hash algorithm + SHA512 = 0x2, +} + +/// Live Firmware Activation (LFA) policies for Arm CCA used in [`CcaPolicy::lfa_policy`]. +#[open_enum] +#[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, +} + /// 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 @@ -976,6 +1030,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 { From 4b08569a8255436d0c34f311af15ee484694fce2 Mon Sep 17 00:00:00 2001 From: "Jiong Wang (Arm Ltd)" Date: Mon, 17 Nov 2025 15:50:39 +0000 Subject: [PATCH 2/4] arm64: cca: address review feedbacks Use fixed structure type for VPContext --- igvm/src/lib.rs | 105 +++++++++++++++++++++++++++++++++++++++--- igvm/src/registers.rs | 73 +---------------------------- igvm_defs/src/lib.rs | 26 +++++++++++ 3 files changed, 126 insertions(+), 78 deletions(-) diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index e0afff2..e619a6d 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -733,6 +733,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, @@ -978,6 +983,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::(), @@ -1018,6 +1024,9 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::AArch64VbsVpContext { .. } => { IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT } + IgvmDirectiveHeader::AArch64CcaVpContext { .. } => { + IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT + } IgvmDirectiveHeader::ParameterInsert(_) => { IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT } @@ -1273,6 +1282,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, @@ -1468,6 +1507,9 @@ impl IgvmDirectiveHeader { AArch64VbsVpContext { compatibility_mask, .. } => Some(*compatibility_mask), + AArch64CcaVpContext { + compatibility_mask, .. + } => Some(*compatibility_mask), ParameterInsert(info) => Some(info.compatibility_mask), ErrorRange { compatibility_mask, .. @@ -1516,6 +1558,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, .. @@ -1672,6 +1717,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)); @@ -1791,7 +1841,7 @@ impl IgvmDirectiveHeader { .ok_or(BinaryHeaderError::InvalidVariableHeaderSize)?; match compatibility_mask_to_platforms(header.compatibility_mask) { - Some(IgvmPlatformType::VSM_ISOLATION) | Some(IgvmPlatformType::CCA) => { + Some(IgvmPlatformType::VSM_ISOLATION) => { // First read the VbsVpContextHeader at file offset let start = (header.file_offset - file_data_start) as usize; let (VbsVpContextHeader { register_count }, remaining_data) = @@ -1913,6 +1963,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); @@ -2645,6 +2724,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 => { @@ -3366,7 +3457,8 @@ impl IgvmFile { | SnpIdBlock { .. } | VbsMeasurement { .. } | X64VbsVpContext { .. } - | AArch64VbsVpContext { .. } => {} + | AArch64VbsVpContext { .. } + | AArch64CcaVpContext { .. } => {} ParameterArea { parameter_area_index, .. @@ -3616,7 +3708,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]; @@ -3660,6 +3752,7 @@ mod tests { 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, @@ -3680,10 +3773,10 @@ mod tests { new_parameter_area(0), new_parameter_usage(0), new_parameter_insert(20, 0, 1), - IgvmDirectiveHeader::AArch64VbsVpContext { - vtl: Vtl::Vtl0, - registers: vec![AArch64Register::X0(0x1234)], + IgvmDirectiveHeader::AArch64CcaVpContext { compatibility_mask: 0x1, + vp_index: 0xabcd, + context, }, ], }; diff --git a/igvm/src/registers.rs b/igvm/src/registers.rs index 48b69a8..0f2e346 100644 --- a/igvm/src/registers.rs +++ b/igvm/src/registers.rs @@ -178,6 +178,7 @@ 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), @@ -189,30 +190,6 @@ pub enum AArch64Register { X5(u64), X6(u64), X7(u64), - X8(u64), - X9(u64), - X10(u64), - X11(u64), - X12(u64), - X13(u64), - X14(u64), - X15(u64), - X16(u64), - X17(u64), - X18(u64), - X19(u64), - X20(u64), - X21(u64), - X22(u64), - X23(u64), - X24(u64), - X25(u64), - X26(u64), - X27(u64), - X28(u64), - XFp(u64), - XLr(u64), - XSp(u64), Cpsr(u64), SctlrEl1(u64), TcrEl1(u64), @@ -234,30 +211,6 @@ impl AArch64Register { AArch64Register::X5(reg) => (HvArm64RegisterName::X5, reg.into()), AArch64Register::X6(reg) => (HvArm64RegisterName::X6, reg.into()), AArch64Register::X7(reg) => (HvArm64RegisterName::X7, reg.into()), - AArch64Register::X8(reg) => (HvArm64RegisterName::X8, reg.into()), - AArch64Register::X9(reg) => (HvArm64RegisterName::X9, reg.into()), - AArch64Register::X10(reg) => (HvArm64RegisterName::X10, reg.into()), - AArch64Register::X11(reg) => (HvArm64RegisterName::X11, reg.into()), - AArch64Register::X12(reg) => (HvArm64RegisterName::X12, reg.into()), - AArch64Register::X13(reg) => (HvArm64RegisterName::X13, reg.into()), - AArch64Register::X14(reg) => (HvArm64RegisterName::X14, reg.into()), - AArch64Register::X15(reg) => (HvArm64RegisterName::X15, reg.into()), - AArch64Register::X16(reg) => (HvArm64RegisterName::X16, reg.into()), - AArch64Register::X17(reg) => (HvArm64RegisterName::X17, reg.into()), - AArch64Register::X18(reg) => (HvArm64RegisterName::X18, reg.into()), - AArch64Register::X19(reg) => (HvArm64RegisterName::X19, reg.into()), - AArch64Register::X20(reg) => (HvArm64RegisterName::X20, reg.into()), - AArch64Register::X21(reg) => (HvArm64RegisterName::X21, reg.into()), - AArch64Register::X22(reg) => (HvArm64RegisterName::X22, reg.into()), - AArch64Register::X23(reg) => (HvArm64RegisterName::X23, reg.into()), - AArch64Register::X24(reg) => (HvArm64RegisterName::X24, reg.into()), - AArch64Register::X25(reg) => (HvArm64RegisterName::X25, reg.into()), - AArch64Register::X26(reg) => (HvArm64RegisterName::X26, reg.into()), - AArch64Register::X27(reg) => (HvArm64RegisterName::X27, reg.into()), - AArch64Register::X28(reg) => (HvArm64RegisterName::X28, reg.into()), - AArch64Register::XFp(reg) => (HvArm64RegisterName::XFp, reg.into()), - AArch64Register::XLr(reg) => (HvArm64RegisterName::XLr, reg.into()), - AArch64Register::XSp(reg) => (HvArm64RegisterName::XSp, reg.into()), AArch64Register::Cpsr(reg) => (HvArm64RegisterName::Cpsr, reg.into()), AArch64Register::SctlrEl1(reg) => (HvArm64RegisterName::SctlrEl1, reg.into()), AArch64Register::TcrEl1(reg) => (HvArm64RegisterName::TcrEl1, reg.into()), @@ -353,30 +306,6 @@ impl TryFrom for AArch64Register { HvArm64RegisterName::X5 => Self::X1(register_value.as_u64()), HvArm64RegisterName::X6 => Self::X1(register_value.as_u64()), HvArm64RegisterName::X7 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X8 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X9 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X10 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X11 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X12 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X13 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X14 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X15 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X16 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X17 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X18 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X19 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X20 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X21 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X22 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X23 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X24 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X25 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X26 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X27 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::X28 => Self::X1(register_value.as_u64()), - HvArm64RegisterName::XFp => Self::X1(register_value.as_u64()), - HvArm64RegisterName::XLr => Self::X1(register_value.as_u64()), - HvArm64RegisterName::XSp => Self::X1(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_defs/src/lib.rs b/igvm_defs/src/lib.rs index 3dadf79..2583d66 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -1057,6 +1057,32 @@ pub struct VbsVpContextRegister { const_assert_eq!(size_of::(), 0x20); +/// Format of [`IGVM_VHS_VP_CONTEXT`] file data for a native ARM64 CCA image. +/// Registers not 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, +} + /// 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 From fedd5e12166166342ba6e3d0b5d82a7d2fb26ff7 Mon Sep 17 00:00:00 2001 From: "Jiong Wang (Arm Ltd)" Date: Wed, 4 Mar 2026 17:09:08 +0000 Subject: [PATCH 3/4] add RMM spec link in some headers --- igvm_defs/src/lib.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 2583d66..4461d4c 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -529,6 +529,8 @@ pub struct CcaPolicy { } /// 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] #[repr(u8)] pub enum CcaHashAlgorithm { @@ -1058,8 +1060,15 @@ pub struct VbsVpContextRegister { const_assert_eq!(size_of::(), 0x20); /// Format of [`IGVM_VHS_VP_CONTEXT`] file data for a native ARM64 CCA image. -/// Registers not specified here are initialized to their architectural -/// reset values. +/// +/// The VP Context corresponds to the REC (Realm Execution Context) in CCA. +/// Therefore, the registers listed below match those defined in the RMM +/// specification, section "B4.6.69 RmiRecParams type", with one exception: +/// `mpidr` is not included, as 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 { From c424f524af6c01ce123148c3a021de0169f3e512 Mon Sep 17 00:00:00 2001 From: "Jiong Wang (Arm Ltd)" Date: Wed, 4 Mar 2026 22:43:03 +0000 Subject: [PATCH 4/4] address review comments from Chris - Add RMM spec section link in comments - Add 'flags' in VPContext for Cca to align with RMM REC def - Use enum as field types --- igvm_defs/src/lib.rs | 78 ++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 4461d4c..048e25a 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -508,30 +508,11 @@ pub struct TdxPolicy { pub reserved: u64, } -/// The Arm CCA policy used in [`IGVM_VHS_GUEST_POLICY::policy`]. -#[bitfield(u64)] -#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] -pub struct CcaPolicy { - /// Whether debug is allowed for the Realm. - #[bits(1)] - pub debug_allowed: u8, - /// Hash algorithm to measure the initial state of the Realm. - #[bits(8)] - pub hash_algorithm: u8, - /// Live Firmware Activation (LFA) policy for the components within the Realm's TCB. - #[bits(2)] - pub lfa_policy: u8, - /// Whether the Memory Encryption Context (MEC) is shared. - #[bits(1)] - pub mec_shared: u8, - #[bits(52)] - 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 @@ -542,8 +523,19 @@ pub enum CcaHashAlgorithm { SHA512 = 0x2, } +impl CcaHashAlgorithm { + const fn from_bits(bits: u8) -> Self { + Self(bits) + } + + const fn into_bits(self) -> u8 { + self.0 + } +} + /// 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 @@ -554,6 +546,36 @@ pub enum CcaLfaPolicy { LFA_ALLOW = 0x1, } +impl CcaLfaPolicy { + const fn from_bits(bits: u8) -> Self { + Self(bits) + } + + const fn into_bits(self) -> u8 { + self.0 + } +} + +/// The Arm CCA policy used in [`IGVM_VHS_GUEST_POLICY::policy`]. +#[bitfield(u64)] +#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)] +pub struct CcaPolicy { + /// Whether debug is allowed for the Realm. + #[bits(1)] + pub debug_allowed: u8, + /// Hash algorithm to measure the initial state of the Realm. + #[bits(8)] + pub hash_algorithm: CcaHashAlgorithm, + /// Live Firmware Activation (LFA) policy for the components within the Realm's TCB. + #[bits(2)] + pub lfa_policy: CcaLfaPolicy, + /// Whether the Memory Encryption Context (MEC) is shared. + #[bits(1)] + pub mec_shared: u8, + #[bits(52)] + pub reserved: u64, +} + /// 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 @@ -1062,10 +1084,16 @@ 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 registers listed below match those defined in the RMM -/// specification, section "B4.6.69 RmiRecParams type", with one exception: -/// `mpidr` is not included, as it is determined by the host and does not need -/// to be specified in the IGVM file. +/// 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. @@ -1090,6 +1118,8 @@ pub struct IgvmVpContextAArch64Cca { 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