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
160 changes: 158 additions & 2 deletions igvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IsolationType> for igvm_defs::IgvmPlatformType {
Expand All @@ -75,6 +77,7 @@ impl From<IsolationType> for igvm_defs::IgvmPlatformType {
IsolationType::Tdx => IgvmPlatformType::TDX,
IsolationType::Sev => IgvmPlatformType::SEV,
IsolationType::SevEs => IgvmPlatformType::SEV_ES,
IsolationType::Cca => IgvmPlatformType::CCA,
}
}
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -725,6 +733,11 @@ pub enum IgvmDirectiveHeader {
registers: Vec<AArch64Register>,
compatibility_mask: u32,
},
AArch64CcaVpContext {
compatibility_mask: u32,
vp_index: u16,
context: Box<IgvmVpContextAArch64Cca>,
},
ParameterInsert(IGVM_VHS_PARAMETER_INSERT),
ErrorRange {
gpa: u64,
Expand Down Expand Up @@ -970,6 +983,7 @@ impl IgvmDirectiveHeader {
IgvmDirectiveHeader::X64NativeVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
IgvmDirectiveHeader::X64VbsVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
IgvmDirectiveHeader::AArch64VbsVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
IgvmDirectiveHeader::AArch64CcaVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
IgvmDirectiveHeader::ParameterInsert(param) => size_of_val(param),
IgvmDirectiveHeader::ErrorRange { .. } => size_of::<IGVM_VHS_ERROR_RANGE>(),
IgvmDirectiveHeader::SnpIdBlock { .. } => size_of::<IGVM_VHS_SNP_ID_BLOCK>(),
Expand Down Expand Up @@ -1010,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
}
Expand Down Expand Up @@ -1265,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<u8> = 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,
Expand Down Expand Up @@ -1460,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, ..
Expand Down Expand Up @@ -1508,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, ..
Expand Down Expand Up @@ -1664,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));
Expand Down Expand Up @@ -1905,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::<IgvmVpContextAArch64Cca>());
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);
Expand Down Expand Up @@ -2374,6 +2461,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),
}

Expand Down Expand Up @@ -2629,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 => {
Expand Down Expand Up @@ -3350,7 +3457,8 @@ impl IgvmFile {
| SnpIdBlock { .. }
| VbsMeasurement { .. }
| X64VbsVpContext { .. }
| AArch64VbsVpContext { .. } => {}
| AArch64VbsVpContext { .. }
| AArch64CcaVpContext { .. } => {}
ParameterArea {
parameter_area_index,
..
Expand Down Expand Up @@ -3463,6 +3571,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,
Expand Down Expand Up @@ -3593,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];
Expand Down Expand Up @@ -3631,6 +3746,47 @@ 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![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::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
}
Expand Down
19 changes: 19 additions & 0 deletions igvm/src/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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()),
Expand Down Expand Up @@ -287,6 +300,12 @@ impl TryFrom<igvm_defs::VbsVpContextRegister> 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::Cpsr => Self::Cpsr(register_value.as_u64()),
HvArm64RegisterName::SctlrEl1 => Self::SctlrEl1(register_value.as_u64()),
HvArm64RegisterName::TcrEl1 => Self::TcrEl1(register_value.as_u64()),
Expand Down
Loading