igvmfilegen/
measurement_diag.rs1use bitfield_struct::bitfield;
15use igvm::IgvmFile;
16use igvm::IgvmInitializationHeader;
17use igvm_defs::IgvmPlatformType;
18use igvm_defs::VbsDigestAlgorithm;
19use igvm_defs::VbsSigningAlgorithm;
20use x86defs::snp::SnpPspIdBlock;
21use zerocopy::Immutable;
22use zerocopy::IntoBytes;
23use zerocopy::KnownLayout;
24
25use crate::snp_id_block::SNP_FAMILY_ID;
26use crate::snp_id_block::SNP_IMAGE_ID;
27
28#[repr(C)]
31#[derive(IntoBytes, Immutable, KnownLayout, Debug)]
32struct VBS_VM_BOOT_MEASUREMENT_SIGNED_DATA {
33 version: u32,
34 product_id: u32,
35 module_id: u32,
36 security_version: u32,
37 security_policy: VBS_POLICY_FLAGS,
38 boot_digest_algo: u32,
39 signing_algo: u32,
40 boot_measurement_digest: [u8; 32],
41}
42
43#[bitfield(u32)]
45#[derive(IntoBytes, Immutable, KnownLayout)]
46#[expect(non_camel_case_types)]
47struct VBS_POLICY_FLAGS {
48 #[bits(1)]
50 debug: bool,
51 #[bits(31)]
52 reserved: u32,
53}
54
55pub fn log_measurement_diagnostic(
58 platform: IgvmPlatformType,
59 digest: &[u8],
60 svn: u32,
61 enable_debug: bool,
62 file: &IgvmFile,
63 compatibility_mask: u32,
64) {
65 match platform {
66 IgvmPlatformType::VSM_ISOLATION => log_vbs(digest, svn, enable_debug),
67 IgvmPlatformType::SEV_SNP => log_snp(digest, svn, file, compatibility_mask),
68 IgvmPlatformType::TDX => log_tdx(digest),
69 _ => {}
70 }
71}
72
73fn log_vbs(digest: &[u8], svn: u32, enable_debug: bool) {
74 const MSFT_PRODUCT_ID: u32 = u32::from_le_bytes(*b"msft");
75 const VBS_MODULE_ID: u32 = u32::from_le_bytes(*b"vbs\0");
76 const VBS_VM_BOOT_MEASUREMENT_VERSION_CURRENT: u32 = 0x1;
77
78 let boot_measurement_digest =
82 <[u8; 32]>::try_from(digest).expect("VBS launch digest is 32 bytes");
83
84 let boot_measurement = VBS_VM_BOOT_MEASUREMENT_SIGNED_DATA {
85 version: VBS_VM_BOOT_MEASUREMENT_VERSION_CURRENT,
86 product_id: MSFT_PRODUCT_ID,
87 module_id: VBS_MODULE_ID,
88 security_version: svn,
89 security_policy: VBS_POLICY_FLAGS::new().with_debug(enable_debug),
90 boot_digest_algo: VbsDigestAlgorithm::SHA256.0,
91 signing_algo: VbsSigningAlgorithm::ECDSA_P384.0,
92 boot_measurement_digest,
93 };
94 tracing::info!("Boot Measurement {:x?}", boot_measurement);
95}
96
97fn log_snp(digest: &[u8], svn: u32, file: &IgvmFile, compatibility_mask: u32) {
98 let ld = <[u8; 48]>::try_from(digest).expect("SNP launch digest is 48 bytes");
102
103 let policy = file
104 .initializations()
105 .iter()
106 .find_map(|h| match h {
107 IgvmInitializationHeader::GuestPolicy {
108 policy,
109 compatibility_mask: mask,
110 } if mask & compatibility_mask == compatibility_mask => Some(*policy),
111 _ => None,
112 })
113 .unwrap_or_else(|| {
114 tracing::error!(
115 compatibility_mask = format_args!("0x{compatibility_mask:X}"),
116 "Missing SNP GuestPolicy initialization header; reporting policy as 0"
117 );
118 0
119 });
120
121 let psp_id_block = SnpPspIdBlock {
122 ld,
123 family_id: SNP_FAMILY_ID,
124 image_id: SNP_IMAGE_ID,
125 version: 0x1,
126 guest_svn: svn,
127 policy,
128 };
129 tracing::info!("SNP ID Block {:x?}", psp_id_block);
130}
131
132fn log_tdx(digest: &[u8]) {
133 tracing::info!("MRTD: {}", hex::encode_upper(digest));
134}