1#![deny(unsafe_code)]
14#![cfg_attr(docsrs, feature(doc_cfg))]
17
18use hv_defs::HvArm64RegisterName;
19use hv_defs::HvX64RegisterName;
20use hv_defs::Vtl;
21use igvm_defs::*;
22use page_table::PageTableRelocationBuilder;
23use range_map_vec::RangeMap;
24use registers::AArch64Register;
25use registers::X86Register;
26use snp_defs::SevVmsa;
27use std::collections::BTreeMap;
28use std::collections::BTreeSet;
29use std::collections::HashMap;
30use std::fmt;
31use std::mem::size_of;
32use std::mem::size_of_val;
33use thiserror::Error;
34use zerocopy::FromBytes;
35use zerocopy::FromZeros;
36use zerocopy::Immutable;
37use zerocopy::IntoBytes;
38use zerocopy::KnownLayout;
39
40#[cfg(feature = "igvm-c")]
41pub mod c_api;
42
43pub mod hv_defs;
44pub mod page_table;
45pub mod registers;
46pub mod snp_defs;
47
48#[allow(non_camel_case_types)]
50type u64_le = zerocopy::U64<zerocopy::LittleEndian>;
51
52#[derive(Debug, PartialEq, Eq)]
54pub enum IsolationType {
55 NotIsolated,
57 Vbs,
59 Snp,
61 Tdx,
63 Sev,
65 SevEs,
67}
68
69impl From<IsolationType> for igvm_defs::IgvmPlatformType {
70 fn from(typ: IsolationType) -> Self {
71 match typ {
72 IsolationType::NotIsolated => IgvmPlatformType::NATIVE,
73 IsolationType::Vbs => IgvmPlatformType::VSM_ISOLATION,
74 IsolationType::Snp => IgvmPlatformType::SEV_SNP,
75 IsolationType::Tdx => IgvmPlatformType::TDX,
76 IsolationType::Sev => IgvmPlatformType::SEV,
77 IsolationType::SevEs => IgvmPlatformType::SEV_ES,
78 }
79 }
80}
81
82fn align_8(x: usize) -> usize {
84 (x + 7) & !7
85}
86
87fn read_header<T: FromBytes + Immutable + KnownLayout>(
92 bytes: &mut &[u8],
93) -> Result<T, BinaryHeaderError> {
94 T::read_from_prefix(bytes)
95 .map_err(|_| BinaryHeaderError::InvalidVariableHeaderSize) .map(|(header, remaining)| {
97 *bytes = remaining;
98 header
99 })
100}
101
102fn append_header<T: IntoBytes + Immutable + KnownLayout>(
105 header: &T,
106 header_type: IgvmVariableHeaderType,
107 variable_headers: &mut Vec<u8>,
108) {
109 let header = header.as_bytes();
110
111 let fixed_header = IGVM_VHS_VARIABLE_HEADER {
116 typ: header_type,
117 length: header
118 .len()
119 .try_into()
120 .expect("header data must fit in u32"),
121 };
122
123 let align_up_iter = std::iter::repeat_n(&0u8, align_8(header.len()) - header.len());
124
125 variable_headers.extend_from_slice(fixed_header.as_bytes());
126 variable_headers.extend_from_slice(header);
127 variable_headers.extend(align_up_iter);
128
129 debug_assert!(variable_headers.len() % 8 == 0);
130}
131
132pub struct FileDataSerializer {
136 file_offset: usize,
138 file_data: Vec<u8>,
140 file_data_map: HashMap<Vec<u8>, u32>,
142}
143
144impl FileDataSerializer {
145 pub fn new(file_offset: usize) -> Self {
148 Self {
149 file_offset,
150 file_data: Vec::new(),
151 file_data_map: HashMap::new(),
152 }
153 }
154
155 pub fn take(self) -> Vec<u8> {
157 self.file_data
158 }
159
160 pub fn write_file_data(&mut self, file_data: &[u8]) -> u32 {
163 if let Some(offset) = self.file_data_map.get(file_data) {
164 return *offset;
165 }
166
167 let offset = self.file_offset;
168 self.file_offset += file_data.len();
169 self.file_data.extend_from_slice(file_data);
170
171 let offset: u32 = offset.try_into().expect("file data offset must fit in u32");
172 self.file_data_map.insert(file_data.to_vec(), offset);
173
174 offset
175 }
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum IgvmPlatformHeader {
182 SupportedPlatform(IGVM_VHS_SUPPORTED_PLATFORM),
183}
184
185impl IgvmPlatformHeader {
186 fn header_size(&self) -> usize {
188 let additional = match self {
189 IgvmPlatformHeader::SupportedPlatform(platform) => size_of_val(platform),
190 };
191
192 size_of::<IGVM_VHS_VARIABLE_HEADER>() + additional
193 }
194
195 #[cfg(feature = "igvm-c")]
197 #[cfg_attr(docsrs, doc(cfg(feature = "igvm-c")))]
198 fn header_type(&self) -> IgvmVariableHeaderType {
199 match self {
200 IgvmPlatformHeader::SupportedPlatform(_) => {
201 IgvmVariableHeaderType::IGVM_VHT_SUPPORTED_PLATFORM
202 }
203 }
204 }
205
206 fn validate(&self) -> Result<(), BinaryHeaderError> {
208 match self {
209 IgvmPlatformHeader::SupportedPlatform(info) => {
210 if info.compatibility_mask.count_ones() != 1 {
212 return Err(BinaryHeaderError::InvalidCompatibilityMask);
213 }
214
215 if info.highest_vtl != 0 && info.highest_vtl != 2 {
217 return Err(BinaryHeaderError::InvalidVtl);
218 }
219
220 match info.platform_type {
222 IgvmPlatformType::NATIVE => {
223 if info.platform_version != IGVM_NATIVE_PLATFORM_VERSION {
224 return Err(BinaryHeaderError::InvalidPlatformVersion);
225 }
226 if info.shared_gpa_boundary != 0 {
227 return Err(BinaryHeaderError::InvalidSharedGpaBoundary);
228 }
229 }
230 IgvmPlatformType::VSM_ISOLATION => {
231 if info.platform_version != IGVM_VSM_ISOLATION_PLATFORM_VERSION {
232 return Err(BinaryHeaderError::InvalidPlatformVersion);
233 }
234
235 if info.shared_gpa_boundary != 0 {
236 return Err(BinaryHeaderError::InvalidSharedGpaBoundary);
237 }
238 }
239 IgvmPlatformType::SEV_SNP => {
240 if info.platform_version != IGVM_SEV_SNP_PLATFORM_VERSION {
241 return Err(BinaryHeaderError::InvalidPlatformVersion);
242 }
243
244 }
246
247 IgvmPlatformType::TDX => {
248 if info.platform_version != IGVM_TDX_PLATFORM_VERSION {
249 return Err(BinaryHeaderError::InvalidPlatformVersion);
250 }
251 }
253 IgvmPlatformType::SEV => {
254 if info.platform_version != IGVM_SEV_PLATFORM_VERSION {
255 return Err(BinaryHeaderError::InvalidPlatformVersion);
256 }
257 }
259 IgvmPlatformType::SEV_ES => {
260 if info.platform_version != IGVM_SEV_ES_PLATFORM_VERSION {
261 return Err(BinaryHeaderError::InvalidPlatformVersion);
262 }
263 }
265 _ => {
266 return Err(BinaryHeaderError::InvalidPlatformType);
267 }
268 }
269
270 Ok(())
271 }
272 }
273 }
274
275 fn new_from_binary_split(
278 mut variable_headers: &[u8],
279 ) -> Result<(Self, &[u8]), BinaryHeaderError> {
280 let header = read_header::<IGVM_VHS_VARIABLE_HEADER>(&mut variable_headers)?;
281
282 if header.typ == IgvmVariableHeaderType::IGVM_VHT_SUPPORTED_PLATFORM
283 && header.length == size_of::<IGVM_VHS_SUPPORTED_PLATFORM>() as u32
284 {
285 let header = IgvmPlatformHeader::SupportedPlatform(read_header(&mut variable_headers)?);
286 header.validate()?;
287
288 Ok((header, variable_headers))
289 } else {
290 Err(BinaryHeaderError::InvalidVariableHeaderType)
291 }
292 }
293
294 #[cfg(feature = "igvm-c")]
299 #[cfg_attr(docsrs, doc(cfg(feature = "igvm-c")))]
300 fn write_binary_header(&self, variable_headers: &mut Vec<u8>) -> Result<(), BinaryHeaderError> {
301 self.validate()?;
303
304 match self {
305 IgvmPlatformHeader::SupportedPlatform(platform) => {
306 append_header(
307 platform,
308 IgvmVariableHeaderType::IGVM_VHT_SUPPORTED_PLATFORM,
309 variable_headers,
310 );
311 }
312 }
313 Ok(())
314 }
315}
316
317#[derive(Debug, Clone, PartialEq, Eq)]
320pub enum IgvmInitializationHeader {
321 GuestPolicy {
322 policy: u64,
323 compatibility_mask: u32,
324 },
325 RelocatableRegion {
326 compatibility_mask: u32,
327 relocation_alignment: u64,
328 relocation_region_gpa: u64,
329 relocation_region_size: u64,
330 minimum_relocation_gpa: u64,
331 maximum_relocation_gpa: u64,
332 is_vtl2: bool,
333 apply_rip_offset: bool,
334 apply_gdtr_offset: bool,
335 vp_index: u16,
336 vtl: Vtl,
337 },
338 PageTableRelocationRegion {
340 compatibility_mask: u32,
341 gpa: u64,
342 size: u64,
343 used_size: u64,
344 vp_index: u16,
345 vtl: Vtl,
346 },
347}
348
349impl IgvmInitializationHeader {
350 fn header_size(&self) -> usize {
352 let additional = match self {
353 IgvmInitializationHeader::GuestPolicy { .. } => size_of::<IGVM_VHS_GUEST_POLICY>(),
354 IgvmInitializationHeader::RelocatableRegion { .. } => {
355 size_of::<IGVM_VHS_RELOCATABLE_REGION>()
356 }
357 IgvmInitializationHeader::PageTableRelocationRegion { .. } => {
358 size_of::<IGVM_VHS_PAGE_TABLE_RELOCATION>()
359 }
360 };
361
362 size_of::<IGVM_VHS_VARIABLE_HEADER>() + additional
363 }
364
365 #[cfg(feature = "igvm-c")]
367 #[cfg_attr(docsrs, doc(cfg(feature = "igvm-c")))]
368 fn header_type(&self) -> IgvmVariableHeaderType {
369 match self {
370 IgvmInitializationHeader::GuestPolicy { .. } => {
371 IgvmVariableHeaderType::IGVM_VHT_GUEST_POLICY
372 }
373 IgvmInitializationHeader::RelocatableRegion { .. } => {
374 IgvmVariableHeaderType::IGVM_VHT_RELOCATABLE_REGION
375 }
376 IgvmInitializationHeader::PageTableRelocationRegion { .. } => {
377 IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION
378 }
379 }
380 }
381
382 fn validate(&self) -> Result<(), BinaryHeaderError> {
384 match self {
385 IgvmInitializationHeader::GuestPolicy {
386 policy: _,
387 compatibility_mask: _,
388 } => {
389 Ok(())
391 }
392 IgvmInitializationHeader::RelocatableRegion {
393 compatibility_mask: _,
394 relocation_alignment,
395 relocation_region_gpa,
396 relocation_region_size,
397 minimum_relocation_gpa,
398 maximum_relocation_gpa,
399 is_vtl2: _,
400 apply_rip_offset: _,
401 apply_gdtr_offset: _,
402 vp_index: _,
403 vtl: _,
404 } => {
405 if relocation_region_size % PAGE_SIZE_4K != 0 {
406 return Err(BinaryHeaderError::RelocationSize);
407 }
408
409 if relocation_alignment % PAGE_SIZE_4K != 0 {
410 return Err(BinaryHeaderError::RelocationAlignment);
411 }
412
413 if relocation_region_gpa % relocation_alignment != 0 {
414 return Err(BinaryHeaderError::RelocationAddress(*relocation_region_gpa));
415 }
416
417 if minimum_relocation_gpa % relocation_alignment != 0 {
418 return Err(BinaryHeaderError::RelocationAddress(
419 *minimum_relocation_gpa,
420 ));
421 }
422
423 if maximum_relocation_gpa % relocation_alignment != 0 {
424 return Err(BinaryHeaderError::RelocationAddress(
425 *maximum_relocation_gpa,
426 ));
427 }
428
429 Ok(())
430 }
431 IgvmInitializationHeader::PageTableRelocationRegion {
432 compatibility_mask: _,
433 gpa,
434 size,
435 used_size,
436 vp_index: _,
437 vtl: _,
438 } => {
439 if gpa % PAGE_SIZE_4K != 0 {
440 return Err(BinaryHeaderError::UnalignedAddress(*gpa));
441 }
442
443 if size % PAGE_SIZE_4K != 0 {
444 return Err(BinaryHeaderError::UnalignedSize(*size));
445 }
446
447 if used_size % PAGE_SIZE_4K != 0 {
448 return Err(BinaryHeaderError::UnalignedSize(*used_size));
449 }
450
451 if used_size > size {
452 return Err(BinaryHeaderError::InvalidPageTableRegionSize);
453 }
454
455 Ok(())
456 }
457 }
458 }
459
460 fn new_from_binary_split(
463 mut variable_headers: &[u8],
464 ) -> Result<(Self, &[u8]), BinaryHeaderError> {
465 let IGVM_VHS_VARIABLE_HEADER { typ, length } =
466 read_header::<IGVM_VHS_VARIABLE_HEADER>(&mut variable_headers)?;
467
468 tracing::trace!(typ = ?typ, len = ?length, "trying to parse typ, len");
469
470 let length = length as usize;
471
472 let header = match typ {
473 IgvmVariableHeaderType::IGVM_VHT_GUEST_POLICY
474 if length == size_of::<IGVM_VHS_GUEST_POLICY>() =>
475 {
476 let IGVM_VHS_GUEST_POLICY {
477 policy,
478 compatibility_mask,
479 reserved,
480 } = read_header(&mut variable_headers)?;
481
482 if reserved != 0 {
483 return Err(BinaryHeaderError::ReservedNotZero);
484 }
485
486 IgvmInitializationHeader::GuestPolicy {
487 policy,
488 compatibility_mask,
489 }
490 }
491 IgvmVariableHeaderType::IGVM_VHT_RELOCATABLE_REGION
492 if length == size_of::<IGVM_VHS_RELOCATABLE_REGION>() =>
493 {
494 let IGVM_VHS_RELOCATABLE_REGION {
495 compatibility_mask,
496 flags,
497 relocation_alignment,
498 relocation_region_gpa,
499 relocation_region_size,
500 minimum_relocation_gpa,
501 maximum_relocation_gpa,
502 vp_index,
503 vtl,
504 } = read_header(&mut variable_headers)?;
505
506 let is_vtl2 = flags & IGVM_VHF_RELOCATABLE_REGION_IS_VTL2
507 == IGVM_VHF_RELOCATABLE_REGION_IS_VTL2;
508 let apply_gdtr_offset = flags & IGVM_VHF_RELOCATABLE_REGION_APPLY_GDTR
509 == IGVM_VHF_RELOCATABLE_REGION_APPLY_GDTR;
510 let apply_rip_offset = flags & IGVM_VHF_RELOCATABLE_REGION_APPLY_RIP
511 == IGVM_VHF_RELOCATABLE_REGION_APPLY_RIP;
512
513 IgvmInitializationHeader::RelocatableRegion {
514 compatibility_mask,
515 relocation_alignment,
516 relocation_region_gpa,
517 relocation_region_size,
518 minimum_relocation_gpa,
519 maximum_relocation_gpa,
520 is_vtl2,
521 apply_gdtr_offset,
522 apply_rip_offset,
523 vp_index,
524 vtl: vtl.try_into().map_err(|_| BinaryHeaderError::InvalidVtl)?,
525 }
526 }
527 IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION
528 if length == size_of::<IGVM_VHS_PAGE_TABLE_RELOCATION>() =>
529 {
530 let IGVM_VHS_PAGE_TABLE_RELOCATION {
531 gpa,
532 size,
533 used_size,
534 compatibility_mask,
535 reserved,
536 vp_index,
537 vtl,
538 } = read_header(&mut variable_headers)?;
539
540 if reserved != 0 {
541 return Err(BinaryHeaderError::ReservedNotZero);
542 }
543
544 IgvmInitializationHeader::PageTableRelocationRegion {
545 compatibility_mask,
546 gpa,
547 size,
548 used_size,
549 vp_index,
550 vtl: vtl.try_into().map_err(|_| BinaryHeaderError::InvalidVtl)?,
551 }
552 }
553
554 _ => return Err(BinaryHeaderError::InvalidVariableHeaderType),
555 };
556
557 header.validate()?;
558 Ok((header, variable_headers))
559 }
560
561 fn compatibility_mask(&self) -> Option<u32> {
563 use IgvmInitializationHeader::*;
564
565 match self {
566 GuestPolicy {
567 compatibility_mask, ..
568 } => Some(*compatibility_mask),
569 RelocatableRegion {
570 compatibility_mask, ..
571 } => Some(*compatibility_mask),
572 PageTableRelocationRegion {
573 compatibility_mask, ..
574 } => Some(*compatibility_mask),
575 }
576 }
577
578 fn write_binary_header(&self, variable_headers: &mut Vec<u8>) -> Result<(), BinaryHeaderError> {
579 self.validate()?;
581
582 match self {
583 IgvmInitializationHeader::GuestPolicy {
584 policy,
585 compatibility_mask,
586 } => {
587 let info = IGVM_VHS_GUEST_POLICY {
588 policy: *policy,
589 compatibility_mask: *compatibility_mask,
590 reserved: 0,
591 };
592
593 append_header(
594 &info,
595 IgvmVariableHeaderType::IGVM_VHT_GUEST_POLICY,
596 variable_headers,
597 );
598 }
599 IgvmInitializationHeader::RelocatableRegion {
600 compatibility_mask,
601 relocation_alignment,
602 relocation_region_gpa,
603 relocation_region_size,
604 minimum_relocation_gpa,
605 maximum_relocation_gpa,
606 is_vtl2,
607 apply_rip_offset,
608 apply_gdtr_offset,
609 vp_index,
610 vtl,
611 } => {
612 let mut flags = 0;
613
614 if *is_vtl2 {
615 flags |= IGVM_VHF_RELOCATABLE_REGION_IS_VTL2;
616 }
617
618 if *apply_rip_offset {
619 flags |= IGVM_VHF_RELOCATABLE_REGION_APPLY_RIP;
620 }
621
622 if *apply_gdtr_offset {
623 flags |= IGVM_VHF_RELOCATABLE_REGION_APPLY_GDTR;
624 }
625
626 let info = IGVM_VHS_RELOCATABLE_REGION {
627 compatibility_mask: *compatibility_mask,
628 relocation_alignment: *relocation_alignment,
629 relocation_region_gpa: *relocation_region_gpa,
630 relocation_region_size: *relocation_region_size,
631 flags,
632 minimum_relocation_gpa: *minimum_relocation_gpa,
633 maximum_relocation_gpa: *maximum_relocation_gpa,
634 vp_index: *vp_index,
635 vtl: *vtl as u8,
636 };
637
638 append_header(
639 &info,
640 IgvmVariableHeaderType::IGVM_VHT_RELOCATABLE_REGION,
641 variable_headers,
642 );
643 }
644 IgvmInitializationHeader::PageTableRelocationRegion {
645 compatibility_mask,
646 gpa,
647 size,
648 used_size,
649 vp_index,
650 vtl,
651 } => {
652 let info = IGVM_VHS_PAGE_TABLE_RELOCATION {
653 gpa: *gpa,
654 size: *size,
655 used_size: *used_size,
656 compatibility_mask: *compatibility_mask,
657 reserved: 0,
658 vp_index: *vp_index,
659 vtl: *vtl as u8,
660 };
661
662 append_header(
663 &info,
664 IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION,
665 variable_headers,
666 );
667 }
668 }
669
670 Ok(())
671 }
672}
673
674#[derive(Debug, Clone, PartialEq, Eq)]
677pub enum IgvmDirectiveHeader {
678 PageData {
679 gpa: u64,
680 compatibility_mask: u32,
681 flags: IgvmPageDataFlags,
682 data_type: IgvmPageDataType,
683 data: Vec<u8>,
684 },
685 ParameterArea {
686 number_of_bytes: u64,
687 parameter_area_index: u32,
688 initial_data: Vec<u8>,
689 },
690 VpCount(IGVM_VHS_PARAMETER),
691 EnvironmentInfo(IGVM_VHS_PARAMETER),
692 Srat(IGVM_VHS_PARAMETER),
693 Madt(IGVM_VHS_PARAMETER),
694 Slit(IGVM_VHS_PARAMETER),
695 Pptt(IGVM_VHS_PARAMETER),
696 MmioRanges(IGVM_VHS_PARAMETER),
697 MemoryMap(IGVM_VHS_PARAMETER),
698 CommandLine(IGVM_VHS_PARAMETER),
699 DeviceTree(IGVM_VHS_PARAMETER),
700 RequiredMemory {
701 gpa: u64,
702 compatibility_mask: u32,
703 number_of_bytes: u32,
704 vtl2_protectable: bool,
705 },
706 SnpVpContext {
707 gpa: u64,
708 compatibility_mask: u32,
709 vp_index: u16,
710 vmsa: Box<SevVmsa>,
711 },
712 X64NativeVpContext {
713 compatibility_mask: u32,
714 vp_index: u16,
715 context: Box<IgvmNativeVpContextX64>,
716 },
717 X64VbsVpContext {
719 vtl: Vtl,
720 registers: Vec<X86Register>,
721 compatibility_mask: u32,
722 },
723 AArch64VbsVpContext {
724 vtl: Vtl,
725 registers: Vec<AArch64Register>,
726 compatibility_mask: u32,
727 },
728 ParameterInsert(IGVM_VHS_PARAMETER_INSERT),
729 ErrorRange {
730 gpa: u64,
731 compatibility_mask: u32,
732 size_bytes: u32,
733 },
734 SnpIdBlock {
735 compatibility_mask: u32,
736 author_key_enabled: u8,
737 reserved: [u8; 3],
738 ld: [u8; 48],
739 family_id: [u8; 16],
740 image_id: [u8; 16],
741 version: u32,
742 guest_svn: u32,
743 id_key_algorithm: u32,
744 author_key_algorithm: u32,
745 id_key_signature: Box<IGVM_VHS_SNP_ID_BLOCK_SIGNATURE>,
746 id_public_key: Box<IGVM_VHS_SNP_ID_BLOCK_PUBLIC_KEY>,
747 author_key_signature: Box<IGVM_VHS_SNP_ID_BLOCK_SIGNATURE>,
748 author_public_key: Box<IGVM_VHS_SNP_ID_BLOCK_PUBLIC_KEY>,
749 },
750 VbsMeasurement {
751 compatibility_mask: u32,
752 version: u32,
753 product_id: u32,
754 module_id: u32,
755 security_version: u32,
756 policy_flags: u32,
757 boot_digest_algo: u32,
758 signing_algo: u32,
759 boot_measurement_digest: Box<[u8; 64]>,
760 signature: Box<[u8; 256]>,
761 public_key: Box<[u8; 512]>,
762 },
763}
764
765impl fmt::Display for IgvmDirectiveHeader {
766 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767 match self {
768 IgvmDirectiveHeader::PageData {
769 gpa,
770 compatibility_mask,
771 flags,
772 data_type,
773 data: _,
774 } => {
775 writeln!(f, "PageData {{")?;
776 writeln!(f, "\t\tgpa: {:#X}", gpa)?;
777 writeln!(f, "\t\tcompatibility_mask: {:#X}", compatibility_mask)?;
778 writeln!(f, "\t\tflags: {:?}", flags)?;
779 writeln!(f, "\t\tdata_type: {:?}", data_type)?;
780 write!(f, "}}")?;
781 Ok(())
782 }
783 IgvmDirectiveHeader::SnpIdBlock {
784 compatibility_mask,
785 author_key_enabled,
786 reserved: _,
787 ld,
788 family_id,
789 image_id,
790 version,
791 guest_svn,
792 id_key_algorithm,
793 author_key_algorithm,
794 id_key_signature,
795 id_public_key,
796 author_key_signature,
797 author_public_key,
798 } => {
799 writeln!(f, "IGVM_VHS_SNP_ID_BLOCK {{")?;
800 writeln!(f, "\t\tcompatibility_mask: {:#X}", compatibility_mask)?;
801 writeln!(f, "\t\tauthor_key_enabled: {:#X}", author_key_enabled)?;
802 writeln!(f, "\t\tld: {}", hex::encode_upper(ld))?;
803 writeln!(f, "\t\tfamily_id: {:#X}", family_id[0])?;
804 writeln!(f, "\t\timage_id: {:#X}", image_id[0])?;
805 writeln!(f, "\t\tversion: {:#X}", version)?;
806 writeln!(f, "\t\tguest_svn: {:#X}", guest_svn)?;
807 writeln!(f, "\t\tid_key_algorithm: {:#X}", id_key_algorithm)?;
808 writeln!(f, "\t\tauthor_key_algorithm: {:#X}", author_key_algorithm)?;
809 writeln!(
810 f,
811 "\t\tid_block_signature R: 0x{}",
812 hex::encode_upper(id_key_signature.r_comp)
813 )?;
814 writeln!(
815 f,
816 "\t\tid_block_signature S: 0x{}",
817 hex::encode_upper(id_key_signature.s_comp)
818 )?;
819 writeln!(
820 f,
821 "\t\tid_public_key qx: 0x{}",
822 hex::encode_upper(id_public_key.qx)
823 )?;
824 writeln!(
825 f,
826 "\t\tid_public_key qy: 0x{}",
827 hex::encode_upper(id_public_key.qy)
828 )?;
829 writeln!(f, "\t\tid_public_key curve: {:#X}", id_public_key.curve)?;
830 if *author_key_enabled == 0x1 {
831 writeln!(
832 f,
833 "\t\tauthor_block_signature R: 0x{}",
834 hex::encode_upper(author_key_signature.r_comp)
835 )?;
836 writeln!(
837 f,
838 "\t\tauthor_block_signature S: 0x{}",
839 hex::encode_upper(author_key_signature.s_comp)
840 )?;
841 writeln!(
842 f,
843 "\t\tauthor_public_key qx: 0x{}",
844 hex::encode_upper(id_public_key.qx)
845 )?;
846 writeln!(
847 f,
848 "\t\tauthor_public_key qy: 0x{}",
849 hex::encode_upper(author_public_key.qy)
850 )?;
851 writeln!(
852 f,
853 "\t\tauthor_public_key curve: {:#X}",
854 author_public_key.curve
855 )?;
856 }
857 write!(f, "}}")?;
858 Ok(())
859 }
860 IgvmDirectiveHeader::VbsMeasurement {
861 compatibility_mask,
862 version,
863 product_id,
864 module_id,
865 security_version,
866 policy_flags,
867 boot_digest_algo,
868 signing_algo,
869 boot_measurement_digest,
870 signature,
871 public_key,
872 } => {
873 writeln!(f, "IGVM_VHS_VBS_MEASUREMENT {{")?;
874 writeln!(f, "\tcompatibility_mask: {:#X}", compatibility_mask)?;
875 writeln!(f, "\tversion: {:#X}", version)?;
876 writeln!(f, "\tproduct_id: {:#X}", product_id)?;
877 writeln!(f, "\tmodule_id: {:#X}", module_id)?;
878 writeln!(f, "\tsecurity_version: {:#X}", security_version)?;
879 writeln!(f, "\tpolicy_flags: {:#X}", policy_flags)?;
880 writeln!(f, "\tboot_digest_algo: {:#X}", boot_digest_algo)?;
881 writeln!(f, "\tsigning_algo: {:#X}", signing_algo)?;
882 writeln!(
883 f,
884 "\tboot_measurement_digest: {}",
885 hex::encode_upper(boot_measurement_digest.as_ref())
886 )?;
887 writeln!(f, "\tsignature: {}", hex::encode_upper(signature.as_ref()))?;
888 writeln!(
889 f,
890 "\tpublic_key: {}",
891 hex::encode_upper(public_key.as_ref())
892 )?;
893 write!(f, "}}")?;
894 Ok(())
895 }
896 other => write!(f, "{:#X?}", other),
897 }
898 }
899}
900
901#[derive(Debug, Error)]
905pub enum BinaryHeaderError {
906 #[error("address {0} is not aligned")]
907 UnalignedAddress(u64),
908 #[error("size {0} is not aligned")]
909 UnalignedSize(u64),
910 #[error("data is an invalid size")]
911 InvalidDataSize,
912 #[error("invalid variable header size")]
913 InvalidVariableHeaderSize,
914 #[error("invalid variable header type")]
915 InvalidVariableHeaderType,
916 #[error("invalid page data type")]
917 InvalidPageDataType,
918 #[error("invalid vp context platform type")]
919 InvalidVpContextPlatformType,
920 #[error("invalid vmsa")]
921 InvalidVmsa,
922 #[error("invalid VP context")]
923 InvalidContext,
924 #[error("invalid compatibility mask")]
925 InvalidCompatibilityMask,
926 #[error("invalid vtl")]
927 InvalidVtl,
928 #[error("invalid platform type")]
929 InvalidPlatformType,
930 #[error("invalid platform version")]
931 InvalidPlatformVersion,
932 #[error("invalid shared gpa boundary")]
933 InvalidSharedGpaBoundary,
934 #[error("reserved values not zero")]
935 ReservedNotZero,
936 #[error("VBS vp context has no registers")]
937 NoVbsVpContextRegisters,
938 #[error("relocation region size not aligned to 4k")]
939 RelocationSize,
940 #[error("relocation alignment not aligned to 4k")]
941 RelocationAlignment,
942 #[error("relocation address not aligned to alignement")]
943 RelocationAddress(u64),
944 #[error("invalid page table entry size")]
945 InvalidPageTableRegionSize,
946 #[error("unsupported x64 register")]
947 UnsupportedX64Register(#[from] registers::UnsupportedRegister<HvX64RegisterName>),
948 #[error("unsupported AArch64 register")]
949 UnsupportedAArch64Register(#[from] registers::UnsupportedRegister<HvArm64RegisterName>),
950}
951
952impl IgvmDirectiveHeader {
953 fn header_size(&self) -> usize {
955 let additional = match self {
956 IgvmDirectiveHeader::PageData { .. } => size_of::<IGVM_VHS_PAGE_DATA>(),
957 IgvmDirectiveHeader::ParameterArea { .. } => size_of::<IGVM_VHS_PARAMETER_AREA>(),
958 IgvmDirectiveHeader::VpCount(param) => size_of_val(param),
959 IgvmDirectiveHeader::EnvironmentInfo(param) => size_of_val(param),
960 IgvmDirectiveHeader::Srat(param) => size_of_val(param),
961 IgvmDirectiveHeader::Madt(param) => size_of_val(param),
962 IgvmDirectiveHeader::Slit(param) => size_of_val(param),
963 IgvmDirectiveHeader::Pptt(param) => size_of_val(param),
964 IgvmDirectiveHeader::MmioRanges(param) => size_of_val(param),
965 IgvmDirectiveHeader::MemoryMap(param) => size_of_val(param),
966 IgvmDirectiveHeader::CommandLine(param) => size_of_val(param),
967 IgvmDirectiveHeader::DeviceTree(param) => size_of_val(param),
968 IgvmDirectiveHeader::RequiredMemory { .. } => size_of::<IGVM_VHS_REQUIRED_MEMORY>(),
969 IgvmDirectiveHeader::SnpVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
970 IgvmDirectiveHeader::X64NativeVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
971 IgvmDirectiveHeader::X64VbsVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
972 IgvmDirectiveHeader::AArch64VbsVpContext { .. } => size_of::<IGVM_VHS_VP_CONTEXT>(),
973 IgvmDirectiveHeader::ParameterInsert(param) => size_of_val(param),
974 IgvmDirectiveHeader::ErrorRange { .. } => size_of::<IGVM_VHS_ERROR_RANGE>(),
975 IgvmDirectiveHeader::SnpIdBlock { .. } => size_of::<IGVM_VHS_SNP_ID_BLOCK>(),
976 IgvmDirectiveHeader::VbsMeasurement { .. } => size_of::<IGVM_VHS_VBS_MEASUREMENT>(),
977 };
978
979 align_8(size_of::<IGVM_VHS_VARIABLE_HEADER>() + additional)
980 }
981
982 #[cfg(feature = "igvm-c")]
984 #[cfg_attr(docsrs, doc(cfg(feature = "igvm-c")))]
985 fn header_type(&self) -> IgvmVariableHeaderType {
986 match self {
987 IgvmDirectiveHeader::PageData { .. } => IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA,
988 IgvmDirectiveHeader::ParameterArea { .. } => {
989 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA
990 }
991 IgvmDirectiveHeader::VpCount(_) => IgvmVariableHeaderType::IGVM_VHT_VP_COUNT_PARAMETER,
992 IgvmDirectiveHeader::Srat(_) => IgvmVariableHeaderType::IGVM_VHT_SRAT,
993 IgvmDirectiveHeader::Madt(_) => IgvmVariableHeaderType::IGVM_VHT_MADT,
994 IgvmDirectiveHeader::Slit(_) => IgvmVariableHeaderType::IGVM_VHT_SLIT,
995 IgvmDirectiveHeader::Pptt(_) => IgvmVariableHeaderType::IGVM_VHT_PPTT,
996 IgvmDirectiveHeader::MmioRanges(_) => IgvmVariableHeaderType::IGVM_VHT_MMIO_RANGES,
997 IgvmDirectiveHeader::MemoryMap(_) => IgvmVariableHeaderType::IGVM_VHT_MEMORY_MAP,
998 IgvmDirectiveHeader::CommandLine(_) => IgvmVariableHeaderType::IGVM_VHT_COMMAND_LINE,
999 IgvmDirectiveHeader::DeviceTree(_) => IgvmVariableHeaderType::IGVM_VHT_DEVICE_TREE,
1000 IgvmDirectiveHeader::RequiredMemory { .. } => {
1001 IgvmVariableHeaderType::IGVM_VHT_REQUIRED_MEMORY
1002 }
1003 IgvmDirectiveHeader::SnpVpContext { .. } => IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
1004 IgvmDirectiveHeader::X64NativeVpContext { .. } => {
1005 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT
1006 }
1007 IgvmDirectiveHeader::X64VbsVpContext { .. } => {
1008 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT
1009 }
1010 IgvmDirectiveHeader::AArch64VbsVpContext { .. } => {
1011 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT
1012 }
1013 IgvmDirectiveHeader::ParameterInsert(_) => {
1014 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT
1015 }
1016 IgvmDirectiveHeader::ErrorRange { .. } => IgvmVariableHeaderType::IGVM_VHT_ERROR_RANGE,
1017 IgvmDirectiveHeader::SnpIdBlock { .. } => IgvmVariableHeaderType::IGVM_VHT_SNP_ID_BLOCK,
1018 IgvmDirectiveHeader::VbsMeasurement { .. } => {
1019 IgvmVariableHeaderType::IGVM_VHT_VBS_MEASUREMENT
1020 }
1021 IgvmDirectiveHeader::EnvironmentInfo(_) => {
1022 IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER
1023 }
1024 }
1025 }
1026
1027 pub fn write_binary_header(
1032 &self,
1033 variable_headers: &mut Vec<u8>,
1034 file_data: &mut FileDataSerializer,
1035 ) -> Result<(), BinaryHeaderError> {
1036 self.validate()?;
1038
1039 match self {
1040 IgvmDirectiveHeader::PageData {
1041 gpa,
1042 compatibility_mask,
1043 flags,
1044 data_type,
1045 data,
1046 } => {
1047 let file_offset = if data.is_empty() {
1048 0
1050 } else {
1051 assert!(data.len() as u64 <= PAGE_SIZE_4K);
1056
1057 let align_up_iter =
1058 std::iter::repeat_n(&0u8, PAGE_SIZE_4K as usize - data.len());
1059 let data: Vec<u8> = data.iter().chain(align_up_iter).copied().collect();
1060 file_data.write_file_data(&data)
1061 };
1062
1063 let info = IGVM_VHS_PAGE_DATA {
1064 gpa: *gpa,
1065 compatibility_mask: *compatibility_mask,
1066 file_offset,
1067 flags: *flags,
1068 data_type: *data_type,
1069 reserved: 0,
1070 };
1071
1072 append_header(
1073 &info,
1074 IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA,
1075 variable_headers,
1076 );
1077 }
1078 IgvmDirectiveHeader::ParameterArea {
1079 number_of_bytes,
1080 parameter_area_index,
1081 initial_data,
1082 } => {
1083 assert_eq!(number_of_bytes % PAGE_SIZE_4K, 0);
1084
1085 let file_offset = if initial_data.is_empty() {
1086 0
1088 } else {
1089 assert!(initial_data.len() as u64 <= *number_of_bytes);
1091
1092 let align_up_iter =
1093 std::iter::repeat_n(&0u8, *number_of_bytes as usize - initial_data.len());
1094 let data: Vec<u8> = initial_data.iter().chain(align_up_iter).copied().collect();
1095 file_data.write_file_data(&data)
1096 };
1097
1098 let info = IGVM_VHS_PARAMETER_AREA {
1099 number_of_bytes: *number_of_bytes,
1100 parameter_area_index: *parameter_area_index,
1101 file_offset,
1102 };
1103
1104 append_header(
1105 &info,
1106 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA,
1107 variable_headers,
1108 );
1109 }
1110 IgvmDirectiveHeader::VpCount(param) => {
1111 append_header(
1112 param,
1113 IgvmVariableHeaderType::IGVM_VHT_VP_COUNT_PARAMETER,
1114 variable_headers,
1115 );
1116 }
1117 IgvmDirectiveHeader::EnvironmentInfo(param) => {
1118 append_header(
1119 param,
1120 IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER,
1121 variable_headers,
1122 );
1123 }
1124 IgvmDirectiveHeader::Srat(param) => {
1125 append_header(
1126 param,
1127 IgvmVariableHeaderType::IGVM_VHT_SRAT,
1128 variable_headers,
1129 );
1130 }
1131 IgvmDirectiveHeader::Madt(param) => {
1132 append_header(
1133 param,
1134 IgvmVariableHeaderType::IGVM_VHT_MADT,
1135 variable_headers,
1136 );
1137 }
1138 IgvmDirectiveHeader::Slit(param) => {
1139 append_header(
1140 param,
1141 IgvmVariableHeaderType::IGVM_VHT_SLIT,
1142 variable_headers,
1143 );
1144 }
1145 IgvmDirectiveHeader::Pptt(param) => {
1146 append_header(
1147 param,
1148 IgvmVariableHeaderType::IGVM_VHT_PPTT,
1149 variable_headers,
1150 );
1151 }
1152 IgvmDirectiveHeader::MmioRanges(param) => {
1153 append_header(
1154 param,
1155 IgvmVariableHeaderType::IGVM_VHT_MMIO_RANGES,
1156 variable_headers,
1157 );
1158 }
1159 IgvmDirectiveHeader::MemoryMap(param) => {
1160 append_header(
1161 param,
1162 IgvmVariableHeaderType::IGVM_VHT_MEMORY_MAP,
1163 variable_headers,
1164 );
1165 }
1166 IgvmDirectiveHeader::CommandLine(param) => {
1167 append_header(
1168 param,
1169 IgvmVariableHeaderType::IGVM_VHT_COMMAND_LINE,
1170 variable_headers,
1171 );
1172 }
1173 IgvmDirectiveHeader::DeviceTree(param) => {
1174 append_header(
1175 param,
1176 IgvmVariableHeaderType::IGVM_VHT_DEVICE_TREE,
1177 variable_headers,
1178 );
1179 }
1180 IgvmDirectiveHeader::RequiredMemory {
1181 gpa,
1182 compatibility_mask,
1183 number_of_bytes,
1184 vtl2_protectable,
1185 } => {
1186 assert_eq!(gpa % PAGE_SIZE_4K, 0);
1188 assert_eq!(*number_of_bytes as u64 % PAGE_SIZE_4K, 0);
1189
1190 let info = IGVM_VHS_REQUIRED_MEMORY {
1191 gpa: *gpa,
1192 compatibility_mask: *compatibility_mask,
1193 number_of_bytes: *number_of_bytes,
1194 flags: RequiredMemoryFlags::new().with_vtl2_protectable(*vtl2_protectable),
1195 reserved: 0,
1196 };
1197
1198 append_header(
1199 &info,
1200 IgvmVariableHeaderType::IGVM_VHT_REQUIRED_MEMORY,
1201 variable_headers,
1202 );
1203 }
1204 IgvmDirectiveHeader::SnpVpContext {
1205 gpa,
1206 compatibility_mask,
1207 vp_index,
1208 vmsa,
1209 } => {
1210 assert_eq!(gpa % PAGE_SIZE_4K, 0);
1212
1213 let align_up_iter =
1215 std::iter::repeat_n(&0u8, PAGE_SIZE_4K as usize - vmsa.as_bytes().len());
1216 let data: Vec<u8> = vmsa
1217 .as_bytes()
1218 .iter()
1219 .chain(align_up_iter)
1220 .copied()
1221 .collect();
1222 let file_offset = file_data.write_file_data(&data);
1223
1224 let info = IGVM_VHS_VP_CONTEXT {
1225 gpa: u64_le::new(*gpa),
1226 compatibility_mask: *compatibility_mask,
1227 file_offset,
1228 vp_index: *vp_index,
1229 reserved: 0,
1230 };
1231
1232 append_header(
1233 &info,
1234 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
1235 variable_headers,
1236 );
1237 }
1238 IgvmDirectiveHeader::X64NativeVpContext {
1239 compatibility_mask,
1240 vp_index,
1241 context,
1242 } => {
1243 let align_up_iter =
1245 std::iter::repeat_n(&0u8, PAGE_SIZE_4K as usize - context.as_bytes().len());
1246 let data: Vec<u8> = context
1247 .as_bytes()
1248 .iter()
1249 .chain(align_up_iter)
1250 .copied()
1251 .collect();
1252 let file_offset = file_data.write_file_data(&data);
1253
1254 let info = IGVM_VHS_VP_CONTEXT {
1255 gpa: 0.into(),
1256 compatibility_mask: *compatibility_mask,
1257 file_offset,
1258 vp_index: *vp_index,
1259 reserved: 0,
1260 };
1261
1262 append_header(
1263 &info,
1264 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
1265 variable_headers,
1266 );
1267 }
1268 IgvmDirectiveHeader::X64VbsVpContext {
1269 vtl,
1270 registers,
1271 compatibility_mask,
1272 } => {
1273 let mut data = Vec::new();
1275 let header = VbsVpContextHeader {
1276 register_count: registers
1277 .len()
1278 .try_into()
1279 .expect("reg count must fit in u32"),
1280 };
1281 data.extend_from_slice(header.as_bytes());
1282
1283 for register in registers {
1284 let vbs_reg = register.into_vbs_vp_context_reg(*vtl);
1285 data.extend_from_slice(vbs_reg.as_bytes());
1286 }
1287
1288 let file_offset = file_data.write_file_data(&data);
1289
1290 let info = IGVM_VHS_VP_CONTEXT {
1291 gpa: 0.into(),
1292 compatibility_mask: *compatibility_mask,
1293 file_offset,
1294 vp_index: 0,
1295 reserved: 0,
1296 };
1297
1298 append_header(
1299 &info,
1300 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
1301 variable_headers,
1302 );
1303 }
1304 IgvmDirectiveHeader::AArch64VbsVpContext {
1305 vtl,
1306 registers,
1307 compatibility_mask,
1308 } => {
1309 let mut data = Vec::new();
1310
1311 let header = VbsVpContextHeader {
1313 register_count: registers
1314 .len()
1315 .try_into()
1316 .expect("reg count must fit in u32"),
1317 };
1318 data.extend_from_slice(header.as_bytes());
1319
1320 for register in registers {
1321 let vbs_reg = register.into_vbs_vp_context_reg(*vtl);
1322 data.extend_from_slice(vbs_reg.as_bytes());
1323 }
1324
1325 let file_offset = file_data.write_file_data(&data);
1326
1327 let info = IGVM_VHS_VP_CONTEXT {
1328 gpa: 0.into(),
1329 compatibility_mask: *compatibility_mask,
1330 file_offset,
1331 vp_index: 0,
1332 reserved: 0,
1333 };
1334
1335 append_header(
1336 &info,
1337 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
1338 variable_headers,
1339 );
1340 }
1341 IgvmDirectiveHeader::ParameterInsert(param) => {
1342 assert_eq!(param.gpa % PAGE_SIZE_4K, 0);
1344
1345 append_header(
1346 param,
1347 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT,
1348 variable_headers,
1349 );
1350 }
1351 IgvmDirectiveHeader::ErrorRange { .. } => {
1352 todo!("append ErrorRange")
1353 }
1354 IgvmDirectiveHeader::SnpIdBlock {
1355 compatibility_mask,
1356 author_key_enabled,
1357 reserved,
1358 ld,
1359 family_id,
1360 image_id,
1361 version,
1362 guest_svn,
1363 id_key_algorithm,
1364 author_key_algorithm,
1365 id_key_signature,
1366 id_public_key,
1367 author_key_signature,
1368 author_public_key,
1369 } => {
1370 let id_block = IGVM_VHS_SNP_ID_BLOCK {
1371 compatibility_mask: *compatibility_mask,
1372 author_key_enabled: *author_key_enabled,
1373 reserved: *reserved,
1374 ld: *ld,
1375 family_id: *family_id,
1376 image_id: *image_id,
1377 version: *version,
1378 guest_svn: *guest_svn,
1379 id_key_algorithm: *id_key_algorithm,
1380 author_key_algorithm: *author_key_algorithm,
1381 id_key_signature: **id_key_signature,
1382 id_public_key: **id_public_key,
1383 author_key_signature: **author_key_signature,
1384 author_public_key: **author_public_key,
1385 };
1386 append_header(
1387 &id_block,
1388 IgvmVariableHeaderType::IGVM_VHT_SNP_ID_BLOCK,
1389 variable_headers,
1390 );
1391 }
1392 IgvmDirectiveHeader::VbsMeasurement {
1393 compatibility_mask,
1394 version,
1395 product_id,
1396 module_id,
1397 security_version,
1398 policy_flags,
1399 boot_digest_algo,
1400 signing_algo,
1401 boot_measurement_digest,
1402 signature,
1403 public_key,
1404 } => {
1405 let vbs_measurement = IGVM_VHS_VBS_MEASUREMENT {
1406 compatibility_mask: *compatibility_mask,
1407 version: *version,
1408 product_id: *product_id,
1409 module_id: *module_id,
1410 security_version: *security_version,
1411 policy_flags: *policy_flags,
1412 boot_digest_algo: *boot_digest_algo,
1413 signing_algo: *signing_algo,
1414 boot_measurement_digest: **boot_measurement_digest,
1415 signature: **signature,
1416 public_key: **public_key,
1417 };
1418 append_header(
1419 &vbs_measurement,
1420 IgvmVariableHeaderType::IGVM_VHT_VBS_MEASUREMENT,
1421 variable_headers,
1422 )
1423 }
1424 }
1425
1426 Ok(())
1427 }
1428
1429 pub fn compatibility_mask(&self) -> Option<u32> {
1431 use IgvmDirectiveHeader::*;
1432
1433 match &self {
1434 PageData {
1435 compatibility_mask, ..
1436 } => Some(*compatibility_mask),
1437 ParameterArea { .. } => None,
1438 VpCount(_) => None,
1439 EnvironmentInfo(_) => None,
1440 Srat(_) => None,
1441 Madt(_) => None,
1442 Slit(_) => None,
1443 Pptt(_) => None,
1444 MmioRanges(_) => None,
1445 MemoryMap(_) => None,
1446 CommandLine(_) => None,
1447 DeviceTree(_) => None,
1448 RequiredMemory {
1449 compatibility_mask, ..
1450 } => Some(*compatibility_mask),
1451 SnpVpContext {
1452 compatibility_mask, ..
1453 } => Some(*compatibility_mask),
1454 X64NativeVpContext {
1455 compatibility_mask, ..
1456 } => Some(*compatibility_mask),
1457 X64VbsVpContext {
1458 compatibility_mask, ..
1459 } => Some(*compatibility_mask),
1460 AArch64VbsVpContext {
1461 compatibility_mask, ..
1462 } => Some(*compatibility_mask),
1463 ParameterInsert(info) => Some(info.compatibility_mask),
1464 ErrorRange {
1465 compatibility_mask, ..
1466 } => Some(*compatibility_mask),
1467 SnpIdBlock {
1468 compatibility_mask, ..
1469 } => Some(*compatibility_mask),
1470 VbsMeasurement {
1471 compatibility_mask, ..
1472 } => Some(*compatibility_mask),
1473 }
1474 }
1475
1476 pub fn compatibility_mask_mut(&mut self) -> Option<&mut u32> {
1479 use IgvmDirectiveHeader::*;
1480
1481 match self {
1482 PageData {
1483 compatibility_mask, ..
1484 } => Some(compatibility_mask),
1485 ParameterArea { .. } => None,
1486 VpCount(_) => None,
1487 EnvironmentInfo(_) => None,
1488 Srat(_) => None,
1489 Madt(_) => None,
1490 Slit(_) => None,
1491 Pptt(_) => None,
1492 MmioRanges(_) => None,
1493 MemoryMap(_) => None,
1494 CommandLine(_) => None,
1495 DeviceTree(_) => None,
1496 RequiredMemory {
1497 compatibility_mask, ..
1498 } => Some(compatibility_mask),
1499 SnpVpContext {
1500 compatibility_mask, ..
1501 } => Some(compatibility_mask),
1502 X64NativeVpContext {
1503 compatibility_mask, ..
1504 } => Some(compatibility_mask),
1505 X64VbsVpContext {
1506 compatibility_mask, ..
1507 } => Some(compatibility_mask),
1508 AArch64VbsVpContext {
1509 compatibility_mask, ..
1510 } => Some(compatibility_mask),
1511 ParameterInsert(info) => Some(&mut info.compatibility_mask),
1512 ErrorRange {
1513 compatibility_mask, ..
1514 } => Some(compatibility_mask),
1515 SnpIdBlock {
1516 compatibility_mask, ..
1517 } => Some(compatibility_mask),
1518 VbsMeasurement {
1519 compatibility_mask, ..
1520 } => Some(compatibility_mask),
1521 }
1522 }
1523
1524 pub fn equivalent(&self, other: &Self) -> bool {
1527 match (self, other) {
1528 (
1529 IgvmDirectiveHeader::PageData {
1530 gpa: a_gpa,
1531 flags: a_flags,
1532 data_type: a_data_type,
1533 data: a_data,
1534 compatibility_mask: _,
1535 },
1536 IgvmDirectiveHeader::PageData {
1537 gpa: b_gpa,
1538 flags: b_flags,
1539 data_type: b_data_type,
1540 data: b_data,
1541 compatibility_mask: _,
1542 },
1543 ) => {
1544 a_gpa == b_gpa
1545 && a_flags == b_flags
1546 && a_data_type == b_data_type
1547 && a_data == b_data
1548 }
1549 (
1550 IgvmDirectiveHeader::RequiredMemory {
1551 gpa: a_gpa,
1552 number_of_bytes: a_number_of_bytes,
1553 vtl2_protectable: a_vtl2_protectable,
1554 compatibility_mask: _,
1555 },
1556 IgvmDirectiveHeader::RequiredMemory {
1557 gpa: b_gpa,
1558 number_of_bytes: b_number_of_bytes,
1559 vtl2_protectable: b_vtl2_protectable,
1560 compatibility_mask: _,
1561 },
1562 ) => {
1563 a_gpa == b_gpa
1564 && a_number_of_bytes == b_number_of_bytes
1565 && a_vtl2_protectable == b_vtl2_protectable
1566 }
1567 _ => self == other,
1569 }
1570 }
1571
1572 fn validate(&self) -> Result<(), BinaryHeaderError> {
1574 match self {
1575 IgvmDirectiveHeader::PageData {
1576 gpa,
1577 compatibility_mask: _,
1578 flags: _,
1579 data_type,
1580 data,
1581 } => {
1582 if gpa % PAGE_SIZE_4K != 0 {
1586 return Err(BinaryHeaderError::UnalignedAddress(*gpa));
1587 }
1588
1589 match *data_type {
1591 IgvmPageDataType::NORMAL
1592 | IgvmPageDataType::SECRETS
1593 | IgvmPageDataType::CPUID_DATA
1594 | IgvmPageDataType::CPUID_XF => {}
1595 _ => return Err(BinaryHeaderError::InvalidPageDataType),
1596 }
1597
1598 if data.len() > PAGE_SIZE_4K as usize {
1600 return Err(BinaryHeaderError::InvalidDataSize);
1601 }
1602 }
1603 IgvmDirectiveHeader::ParameterArea {
1604 number_of_bytes,
1605 parameter_area_index: _,
1606 initial_data,
1607 } => {
1608 if number_of_bytes % PAGE_SIZE_4K != 0 {
1609 return Err(BinaryHeaderError::UnalignedSize(*number_of_bytes));
1610 }
1611
1612 if initial_data.len() > *number_of_bytes as usize {
1613 return Err(BinaryHeaderError::InvalidDataSize);
1614 }
1615 }
1616 IgvmDirectiveHeader::VpCount(_)
1619 | IgvmDirectiveHeader::EnvironmentInfo(_)
1620 | IgvmDirectiveHeader::Srat(_)
1621 | IgvmDirectiveHeader::Madt(_)
1622 | IgvmDirectiveHeader::Slit(_)
1623 | IgvmDirectiveHeader::Pptt(_)
1624 | IgvmDirectiveHeader::MmioRanges(_)
1625 | IgvmDirectiveHeader::MemoryMap(_)
1626 | IgvmDirectiveHeader::CommandLine(_)
1627 | IgvmDirectiveHeader::DeviceTree(_) => {}
1628 IgvmDirectiveHeader::RequiredMemory {
1629 gpa,
1630 compatibility_mask: _,
1631 number_of_bytes,
1632 vtl2_protectable: _,
1633 } => {
1634 if gpa % PAGE_SIZE_4K != 0 {
1635 return Err(BinaryHeaderError::UnalignedAddress(*gpa));
1636 }
1637
1638 if *number_of_bytes as u64 % PAGE_SIZE_4K != 0 {
1639 return Err(BinaryHeaderError::UnalignedSize(*number_of_bytes as u64));
1640 }
1641 }
1642 IgvmDirectiveHeader::SnpVpContext {
1643 gpa,
1644 compatibility_mask: _,
1645 vp_index: _,
1646 vmsa: _,
1647 } => {
1648 if gpa % PAGE_SIZE_4K != 0 {
1649 return Err(BinaryHeaderError::UnalignedAddress(*gpa));
1650 }
1651 }
1652 IgvmDirectiveHeader::X64NativeVpContext {
1653 compatibility_mask: _,
1654 vp_index: _,
1655 context: _,
1656 } => {}
1657 IgvmDirectiveHeader::X64VbsVpContext {
1658 vtl: _,
1659 registers: _,
1660 compatibility_mask: _,
1661 } => {}
1662 IgvmDirectiveHeader::AArch64VbsVpContext {
1663 vtl: _,
1664 registers: _,
1665 compatibility_mask: _,
1666 } => {}
1667 IgvmDirectiveHeader::ParameterInsert(param) => {
1668 if param.gpa % PAGE_SIZE_4K != 0 {
1669 return Err(BinaryHeaderError::UnalignedAddress(param.gpa));
1670 }
1671 }
1672 IgvmDirectiveHeader::ErrorRange { gpa, .. } => {
1673 if gpa % PAGE_SIZE_4K != 0 {
1675 return Err(BinaryHeaderError::UnalignedAddress(*gpa));
1676 }
1677 }
1678 IgvmDirectiveHeader::SnpIdBlock { .. } => {}
1680 IgvmDirectiveHeader::VbsMeasurement { .. } => {}
1682 }
1683
1684 Ok(())
1685 }
1686
1687 fn new_from_binary_split<'a>(
1694 revision: IgvmRevision,
1695 mut variable_headers: &'a [u8],
1696 file_data: &'a [u8],
1697 file_data_start: u32,
1698 compatibility_mask_to_platforms: impl Fn(u32) -> Option<IgvmPlatformType>,
1699 ) -> Result<(Self, &'a [u8]), BinaryHeaderError> {
1700 let IGVM_VHS_VARIABLE_HEADER { typ, length } = read_header(&mut variable_headers)?;
1702
1703 tracing::trace!(typ = ?typ, len = ?length, "trying to parse typ, len");
1704
1705 let length = length as usize;
1706 let extract_file_data =
1709 |file_offset: u32, size: usize| -> Result<Vec<u8>, BinaryHeaderError> {
1710 if file_offset == 0 {
1711 return Ok(Vec::new());
1712 }
1713
1714 let start = (file_offset - file_data_start) as usize;
1715 let end = start + size;
1716
1717 file_data
1718 .get(start..end)
1719 .ok_or(BinaryHeaderError::InvalidDataSize)
1720 .map(|slice| slice.to_vec())
1721 };
1722 let header = match typ {
1723 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA
1724 if length == size_of::<IGVM_VHS_PARAMETER_AREA>() =>
1725 {
1726 let IGVM_VHS_PARAMETER_AREA {
1727 file_offset,
1728 number_of_bytes,
1729 parameter_area_index,
1730 } = read_header(&mut variable_headers)?;
1731
1732 let data = extract_file_data(file_offset, number_of_bytes as usize)?;
1733
1734 IgvmDirectiveHeader::ParameterArea {
1735 number_of_bytes,
1736 parameter_area_index,
1737 initial_data: data,
1738 }
1739 }
1740 IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA
1741 if length == size_of::<IGVM_VHS_PAGE_DATA>() =>
1742 {
1743 let IGVM_VHS_PAGE_DATA {
1744 gpa,
1745 compatibility_mask,
1746 flags,
1747 data_type,
1748 file_offset,
1749 reserved,
1750 } = read_header(&mut variable_headers)?;
1751
1752 let data = extract_file_data(file_offset, PAGE_SIZE_4K as usize)?;
1754
1755 if reserved != 0 {
1756 return Err(BinaryHeaderError::ReservedNotZero);
1757 }
1758
1759 IgvmDirectiveHeader::PageData {
1760 gpa,
1761 compatibility_mask,
1762 flags,
1763 data_type,
1764 data,
1765 }
1766 }
1767 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT
1768 if length == size_of::<IGVM_VHS_PARAMETER_INSERT>() =>
1769 {
1770 IgvmDirectiveHeader::ParameterInsert(read_header(&mut variable_headers)?)
1771 }
1772 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT
1773 if length == size_of::<IGVM_VHS_VP_CONTEXT>() =>
1774 {
1775 let header = read_header::<IGVM_VHS_VP_CONTEXT>(&mut &*variable_headers)?;
1778
1779 let aligned_size = align_8(size_of::<IGVM_VHS_VP_CONTEXT>());
1781 variable_headers = variable_headers
1782 .get(aligned_size..)
1783 .ok_or(BinaryHeaderError::InvalidVariableHeaderSize)?;
1784
1785 match compatibility_mask_to_platforms(header.compatibility_mask) {
1786 Some(IgvmPlatformType::VSM_ISOLATION) => {
1787 let start = (header.file_offset - file_data_start) as usize;
1789 let (VbsVpContextHeader { register_count }, remaining_data) =
1790 VbsVpContextHeader::read_from_prefix(&file_data[start..])
1791 .map_err(|_| BinaryHeaderError::InvalidDataSize)?; let mut registers: Vec<VbsVpContextRegister> = Vec::new();
1794 let mut vp_vtl: Option<u8> = None;
1795 let mut remaining_data = remaining_data;
1796
1797 for _ in 0..register_count {
1798 let reg = match VbsVpContextRegister::read_from_prefix(remaining_data) {
1799 Ok((reg, slice)) => {
1800 remaining_data = slice;
1801 reg
1802 }
1803 Err(_) => return Err(BinaryHeaderError::InvalidDataSize), };
1805
1806 registers.push(reg);
1807
1808 match vp_vtl {
1810 Some(vtl) => assert_eq!(vtl, reg.vtl),
1811 None => vp_vtl = Some(reg.vtl),
1812 }
1813 }
1814
1815 let vp_index = header.vp_index;
1817 assert_eq!(vp_index, 0);
1818
1819 let vtl = vp_vtl
1820 .ok_or(BinaryHeaderError::NoVbsVpContextRegisters)?
1821 .try_into()
1822 .map_err(|_| BinaryHeaderError::InvalidVtl)?;
1823
1824 match revision.arch() {
1825 Arch::X64 => {
1826 let registers: Result<Vec<X86Register>, _> = registers
1827 .iter()
1828 .map(|reg| X86Register::try_from(*reg))
1829 .collect();
1830
1831 IgvmDirectiveHeader::X64VbsVpContext {
1832 vtl,
1833 registers: registers?,
1834 compatibility_mask: header.compatibility_mask,
1835 }
1836 }
1837 Arch::AArch64 => {
1838 let registers: Result<Vec<AArch64Register>, _> = registers
1839 .iter()
1840 .map(|reg| AArch64Register::try_from(*reg))
1841 .collect();
1842
1843 IgvmDirectiveHeader::AArch64VbsVpContext {
1844 vtl,
1845 registers: registers?,
1846 compatibility_mask: header.compatibility_mask,
1847 }
1848 }
1849 }
1850 }
1851 Some(IgvmPlatformType::SEV_SNP) | Some(IgvmPlatformType::SEV_ES) => {
1852 let start = (header.file_offset - file_data_start) as usize;
1854 if file_data.len() < start {
1855 return Err(BinaryHeaderError::InvalidDataSize);
1856 }
1857
1858 let data = file_data
1859 .get(start..)
1860 .and_then(|x| x.get(..PAGE_SIZE_4K as usize))
1861 .ok_or(BinaryHeaderError::InvalidDataSize)?;
1862
1863 let mut vmsa = SevVmsa::new_box_zeroed().unwrap();
1866 let (vmsa_slice, remaining) = data.split_at(size_of::<SevVmsa>());
1867 vmsa.as_mut_bytes().copy_from_slice(vmsa_slice);
1868 if remaining.iter().any(|b| *b != 0) {
1869 return Err(BinaryHeaderError::InvalidVmsa);
1870 }
1871
1872 IgvmDirectiveHeader::SnpVpContext {
1873 gpa: header.gpa.into(),
1874 compatibility_mask: header.compatibility_mask,
1875 vp_index: header.vp_index,
1876 vmsa,
1877 }
1878 }
1879 Some(IgvmPlatformType::NATIVE) | Some(IgvmPlatformType::SEV) => {
1880 let start = (header.file_offset - file_data_start) as usize;
1882 if file_data.len() < start {
1883 return Err(BinaryHeaderError::InvalidDataSize);
1884 }
1885
1886 let data = file_data
1887 .get(start..)
1888 .and_then(|x| x.get(..PAGE_SIZE_4K as usize))
1889 .ok_or(BinaryHeaderError::InvalidDataSize)?;
1890
1891 let mut context = IgvmNativeVpContextX64::new_box_zeroed().unwrap();
1895 let (context_slice, remaining) =
1896 data.split_at(size_of::<IgvmNativeVpContextX64>());
1897 context.as_mut_bytes().copy_from_slice(context_slice);
1898 if remaining.iter().any(|b| *b != 0) {
1899 return Err(BinaryHeaderError::InvalidContext);
1900 }
1901
1902 IgvmDirectiveHeader::X64NativeVpContext {
1903 compatibility_mask: header.compatibility_mask,
1904 vp_index: header.vp_index,
1905 context,
1906 }
1907 }
1908 _ => {
1909 return Err(BinaryHeaderError::InvalidVpContextPlatformType);
1911 }
1912 }
1913 }
1914 IgvmVariableHeaderType::IGVM_VHT_REQUIRED_MEMORY
1915 if length == size_of::<IGVM_VHS_REQUIRED_MEMORY>() =>
1916 {
1917 let IGVM_VHS_REQUIRED_MEMORY {
1918 gpa,
1919 compatibility_mask,
1920 number_of_bytes,
1921 flags,
1922 reserved,
1923 } = read_header(&mut variable_headers)?;
1924
1925 if reserved != 0 {
1926 return Err(BinaryHeaderError::ReservedNotZero);
1927 }
1928
1929 let vtl2_protectable = flags.vtl2_protectable();
1930
1931 IgvmDirectiveHeader::RequiredMemory {
1932 gpa,
1933 compatibility_mask,
1934 number_of_bytes,
1935 vtl2_protectable,
1936 }
1937 }
1938 IgvmVariableHeaderType::IGVM_VHT_VP_COUNT_PARAMETER
1939 if length == size_of::<IGVM_VHS_PARAMETER>() =>
1940 {
1941 IgvmDirectiveHeader::VpCount(read_header(&mut variable_headers)?)
1942 }
1943 IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER
1944 if length == size_of::<IGVM_VHS_PARAMETER>() =>
1945 {
1946 IgvmDirectiveHeader::EnvironmentInfo(read_header(&mut variable_headers)?)
1947 }
1948 IgvmVariableHeaderType::IGVM_VHT_SRAT if length == size_of::<IGVM_VHS_PARAMETER>() => {
1949 IgvmDirectiveHeader::Srat(read_header(&mut variable_headers)?)
1950 }
1951 IgvmVariableHeaderType::IGVM_VHT_MADT if length == size_of::<IGVM_VHS_PARAMETER>() => {
1952 IgvmDirectiveHeader::Madt(read_header(&mut variable_headers)?)
1953 }
1954 IgvmVariableHeaderType::IGVM_VHT_SLIT if length == size_of::<IGVM_VHS_PARAMETER>() => {
1955 IgvmDirectiveHeader::Slit(read_header(&mut variable_headers)?)
1956 }
1957 IgvmVariableHeaderType::IGVM_VHT_PPTT if length == size_of::<IGVM_VHS_PARAMETER>() => {
1958 IgvmDirectiveHeader::Pptt(read_header(&mut variable_headers)?)
1959 }
1960 IgvmVariableHeaderType::IGVM_VHT_MMIO_RANGES
1961 if length == size_of::<IGVM_VHS_PARAMETER>() =>
1962 {
1963 IgvmDirectiveHeader::MmioRanges(read_header(&mut variable_headers)?)
1964 }
1965 IgvmVariableHeaderType::IGVM_VHT_SNP_ID_BLOCK
1966 if length == size_of::<IGVM_VHS_SNP_ID_BLOCK>() =>
1967 {
1968 let IGVM_VHS_SNP_ID_BLOCK {
1969 compatibility_mask,
1970 author_key_enabled,
1971 reserved,
1972 ld,
1973 family_id,
1974 image_id,
1975 version,
1976 guest_svn,
1977 id_key_algorithm,
1978 author_key_algorithm,
1979 id_key_signature,
1980 id_public_key,
1981 author_key_signature,
1982 author_public_key,
1983 } = read_header(&mut variable_headers)?;
1984 IgvmDirectiveHeader::SnpIdBlock {
1985 compatibility_mask,
1986 author_key_enabled,
1987 reserved,
1988 ld,
1989 family_id,
1990 image_id,
1991 version,
1992 guest_svn,
1993 id_key_algorithm,
1994 author_key_algorithm,
1995 id_key_signature: Box::new(id_key_signature),
1996 id_public_key: Box::new(id_public_key),
1997 author_key_signature: Box::new(author_key_signature),
1998 author_public_key: Box::new(author_public_key),
1999 }
2000 }
2001 IgvmVariableHeaderType::IGVM_VHT_VBS_MEASUREMENT
2002 if length == size_of::<IGVM_VHS_VBS_MEASUREMENT>() =>
2003 {
2004 let IGVM_VHS_VBS_MEASUREMENT {
2005 compatibility_mask,
2006 version,
2007 product_id,
2008 module_id,
2009 security_version,
2010 policy_flags,
2011 boot_digest_algo,
2012 signing_algo,
2013 boot_measurement_digest,
2014 signature,
2015 public_key,
2016 } = read_header(&mut variable_headers)?;
2017 IgvmDirectiveHeader::VbsMeasurement {
2018 compatibility_mask,
2019 version,
2020 product_id,
2021 module_id,
2022 security_version,
2023 policy_flags,
2024 boot_digest_algo,
2025 signing_algo,
2026 boot_measurement_digest: Box::new(boot_measurement_digest),
2027 signature: Box::new(signature),
2028 public_key: Box::new(public_key),
2029 }
2030 }
2031 IgvmVariableHeaderType::IGVM_VHT_MEMORY_MAP
2032 if length == size_of::<IGVM_VHS_PARAMETER>() =>
2033 {
2034 IgvmDirectiveHeader::MemoryMap(read_header(&mut variable_headers)?)
2035 }
2036 IgvmVariableHeaderType::IGVM_VHT_ERROR_RANGE
2037 if length == size_of::<IGVM_VHS_ERROR_RANGE>() =>
2038 {
2039 let IGVM_VHS_ERROR_RANGE {
2040 gpa,
2041 compatibility_mask,
2042 size_bytes,
2043 } = read_header(&mut variable_headers)?;
2044 IgvmDirectiveHeader::ErrorRange {
2045 gpa,
2046 compatibility_mask,
2047 size_bytes,
2048 }
2049 }
2050 IgvmVariableHeaderType::IGVM_VHT_COMMAND_LINE
2051 if length == size_of::<IGVM_VHS_PARAMETER>() =>
2052 {
2053 IgvmDirectiveHeader::CommandLine(read_header(&mut variable_headers)?)
2054 }
2055 IgvmVariableHeaderType::IGVM_VHT_DEVICE_TREE
2056 if length == size_of::<IGVM_VHS_PARAMETER>() =>
2057 {
2058 IgvmDirectiveHeader::DeviceTree(read_header(&mut variable_headers)?)
2059 }
2060 _ => return Err(BinaryHeaderError::InvalidVariableHeaderType),
2061 };
2062
2063 header.validate()?;
2064 Ok((header, variable_headers))
2065 }
2066}
2067
2068#[derive(Debug, Error)]
2069pub enum Error {
2070 #[error("no valid platform headers")]
2071 NoPlatformHeaders,
2072 #[error("file data section too large")]
2073 FileDataSectionTooLarge,
2074 #[error("variable header section too large")]
2075 VariableHeaderSectionTooLarge,
2076 #[error("total file size too large")]
2077 TotalFileSizeTooLarge,
2078 #[error("invalid binary platform header")]
2079 InvalidBinaryPlatformHeader(#[source] BinaryHeaderError),
2080 #[error("invalid binary initialization header")]
2081 InvalidBinaryInitializationHeader(#[source] BinaryHeaderError),
2082 #[error("invalid binary directive header")]
2083 InvalidBinaryDirectiveHeader(#[source] BinaryHeaderError),
2084 #[error("multiple platform headers with the same isolation type")]
2085 MultiplePlatformHeadersWithSameIsolation,
2086 #[error("invalid parameter area index")]
2087 InvalidParameterAreaIndex,
2088 #[error("invalid platform type")]
2089 InvalidPlatformType,
2090 #[error("no free compatibility masks")]
2091 NoFreeCompatibilityMasks,
2092 #[error("invalid fixed header")]
2093 InvalidFixedHeader,
2094 #[error("invalid binary variable header section")]
2095 InvalidBinaryVariableHeaderSection,
2096 #[error("invalid checksum in fixed header, expected {expected} was {header_value}")]
2097 InvalidChecksum { expected: u32, header_value: u32 },
2098 #[error("page table relocation header specified twice for a compatibiltiy mask")]
2099 MultiplePageTableRelocationHeaders,
2100 #[error("relocation regions overlap")]
2101 RelocationRegionsOverlap,
2102 #[error("parameter insert inside page table region")]
2103 ParameterInsertInsidePageTableRegion,
2104 #[error("no matching vp context for vp index and vtl")]
2105 NoMatchingVpContext,
2106 #[error("platform {platform:?} not supported on architecture {arch:?}")]
2107 PlatformArchUnsupported {
2108 arch: Arch,
2109 platform: igvm_defs::IgvmPlatformType,
2110 },
2111 #[error("invalid header type {header_type} on arch {arch:?}")]
2112 InvalidHeaderArch { arch: Arch, header_type: String },
2113 #[error("page size of 0x{0:x} unsupported")]
2114 UnsupportedPageSize(u32),
2115 #[error("invalid fixed header arch")]
2116 InvalidFixedHeaderArch(u32),
2117 #[error("merged igvm files are not the same revision")]
2118 MergeRevision,
2119}
2120
2121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2123pub enum Arch {
2124 X64,
2125 AArch64,
2126}
2127
2128impl From<Arch> for IgvmArchitecture {
2129 fn from(value: Arch) -> Self {
2130 match value {
2131 Arch::X64 => IgvmArchitecture::X64,
2132 Arch::AArch64 => IgvmArchitecture::AARCH64,
2133 }
2134 }
2135}
2136
2137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2139pub enum IgvmRevision {
2140 V1,
2141 V2 {
2142 arch: Arch,
2144 page_size: u32,
2146 },
2147}
2148
2149impl IgvmRevision {
2150 fn arch(&self) -> Arch {
2151 match self {
2152 IgvmRevision::V1 => Arch::X64,
2153 IgvmRevision::V2 { arch, .. } => *arch,
2154 }
2155 }
2156
2157 fn page_size(&self) -> u64 {
2158 match self {
2159 IgvmRevision::V1 => PAGE_SIZE_4K,
2160 IgvmRevision::V2 { page_size, .. } => *page_size as u64,
2161 }
2162 }
2163
2164 fn fixed_header_size(&self) -> usize {
2165 match self {
2166 IgvmRevision::V1 => size_of::<IGVM_FIXED_HEADER>(),
2167 IgvmRevision::V2 { .. } => size_of::<IGVM_FIXED_HEADER_V2>(),
2168 }
2169 }
2170}
2171
2172#[derive(Debug, Clone)]
2175pub struct IgvmFile {
2176 revision: IgvmRevision,
2177 platform_headers: Vec<IgvmPlatformHeader>,
2178 initialization_headers: Vec<IgvmInitializationHeader>,
2179 directive_headers: Vec<IgvmDirectiveHeader>,
2180}
2181
2182impl fmt::Display for IgvmFile {
2183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2184 writeln!(f, "{:#X?}", self.platform_headers)?;
2185 writeln!(f, "{:#X?}", self.initialization_headers)?;
2186 for h in &self.directive_headers {
2187 writeln!(f, "{}", h)?;
2188 }
2189 Ok(())
2190 }
2191}
2192
2193#[derive(Debug, Clone)]
2195pub struct IgvmRelocatableRegion {
2196 pub base_gpa: u64,
2197 pub size: u64,
2198 pub relocation_alignment: u64,
2199 pub minimum_relocation_gpa: u64,
2200 pub maximum_relocation_gpa: u64,
2201 pub is_vtl2: bool,
2202 pub apply_rip_offset: bool,
2203 pub apply_gdtr_offset: bool,
2204 pub vp_index: u16,
2205 pub vtl: Vtl,
2206}
2207
2208impl IgvmRelocatableRegion {
2209 pub fn contains(&self, gpa: u64) -> bool {
2211 let end = self.base_gpa + self.size;
2212 gpa >= self.base_gpa && gpa < end
2213 }
2214
2215 pub fn relocation_base_valid(&self, relocation_base: u64) -> bool {
2217 let start = relocation_base;
2220 let end = relocation_base + self.size;
2221
2222 if start % self.relocation_alignment != 0 {
2223 tracing::debug!("base is not aligned");
2224 false
2225 } else if start < self.minimum_relocation_gpa {
2226 tracing::debug!("base is too low");
2227 false
2228 } else if end > self.maximum_relocation_gpa {
2229 tracing::debug!("end is too high");
2230 false
2231 } else {
2232 true
2233 }
2234 }
2235}
2236
2237#[derive(Debug, Clone)]
2238struct VpIdentifier {
2239 compatibility_mask: u32,
2240 vp_index: u16,
2241 vtl: Vtl,
2242}
2243
2244#[derive(Debug, Clone)]
2245struct PageTableRegion {
2246 compatibility_mask: u32,
2247 gpa: u64,
2248 size: u64,
2249}
2250
2251#[derive(Debug, Clone)]
2252struct DirectiveHeaderValidationInfo {
2253 used_vp_idents: Vec<VpIdentifier>,
2254 page_table_regions: Vec<PageTableRegion>,
2255}
2256
2257fn extract_individual_masks(mut compatibility_mask: u32) -> Vec<u32> {
2258 let mut masks = Vec::new();
2259 while compatibility_mask != 0 {
2260 let single_mask = 1 << compatibility_mask.trailing_zeros();
2261 masks.push(single_mask);
2262 compatibility_mask &= !single_mask;
2263 }
2264 masks
2265}
2266
2267#[derive(Debug, Clone)]
2269enum FixedHeader {
2270 V1(IGVM_FIXED_HEADER),
2271 V2(IGVM_FIXED_HEADER_V2),
2272}
2273
2274impl FixedHeader {
2275 fn as_bytes(&self) -> &[u8] {
2277 match self {
2278 FixedHeader::V1(raw) => raw.as_bytes(),
2279 FixedHeader::V2(raw) => raw.as_bytes(),
2280 }
2281 }
2282
2283 fn set_total_file_size(&mut self, size: u32) {
2284 match self {
2285 FixedHeader::V1(raw) => raw.total_file_size = size,
2286 FixedHeader::V2(raw) => raw.total_file_size = size,
2287 }
2288 }
2289
2290 fn set_checksum(&mut self, checksum: u32) {
2291 match self {
2292 FixedHeader::V1(raw) => raw.checksum = checksum,
2293 FixedHeader::V2(raw) => raw.checksum = checksum,
2294 }
2295 }
2296
2297 fn magic(&self) -> u32 {
2298 match self {
2299 FixedHeader::V1(raw) => raw.magic,
2300 FixedHeader::V2(raw) => raw.magic,
2301 }
2302 }
2303
2304 fn format_version(&self) -> u32 {
2305 match self {
2306 FixedHeader::V1(raw) => raw.format_version,
2307 FixedHeader::V2(raw) => raw.format_version,
2308 }
2309 }
2310
2311 fn total_file_size(&self) -> u32 {
2312 match self {
2313 FixedHeader::V1(raw) => raw.total_file_size,
2314 FixedHeader::V2(raw) => raw.total_file_size,
2315 }
2316 }
2317
2318 fn variable_header_offset(&self) -> u32 {
2319 match self {
2320 FixedHeader::V1(raw) => raw.variable_header_offset,
2321 FixedHeader::V2(raw) => raw.variable_header_offset,
2322 }
2323 }
2324
2325 fn variable_header_size(&self) -> u32 {
2326 match self {
2327 FixedHeader::V1(raw) => raw.variable_header_size,
2328 FixedHeader::V2(raw) => raw.variable_header_size,
2329 }
2330 }
2331
2332 fn checksum(&self) -> u32 {
2333 match self {
2334 FixedHeader::V1(raw) => raw.checksum,
2335 FixedHeader::V2(raw) => raw.checksum,
2336 }
2337 }
2338}
2339
2340impl IgvmFile {
2341 fn validate_platform_headers<'a>(
2349 revision: IgvmRevision,
2350 platform_headers: impl Iterator<Item = &'a IgvmPlatformHeader>,
2351 ) -> Result<(), Error> {
2352 let mut at_least_one = false;
2353 let mut isolation_types = HashMap::new();
2354
2355 for header in platform_headers {
2356 at_least_one = true;
2357 header
2358 .validate()
2359 .map_err(Error::InvalidBinaryPlatformHeader)?;
2360
2361 match header {
2362 IgvmPlatformHeader::SupportedPlatform(info) => {
2363 match info.platform_type {
2364 IgvmPlatformType::VSM_ISOLATION => {}
2365 IgvmPlatformType::SEV_SNP
2366 | IgvmPlatformType::TDX
2367 | IgvmPlatformType::NATIVE
2368 | IgvmPlatformType::SEV
2369 | IgvmPlatformType::SEV_ES => {
2370 if revision.arch() != Arch::X64 {
2371 return Err(Error::PlatformArchUnsupported {
2372 arch: revision.arch(),
2373 platform: info.platform_type,
2374 });
2375 }
2376 }
2377 _ => return Err(Error::InvalidPlatformType),
2378 }
2379
2380 if let Some(prev) = isolation_types.insert(info.platform_type, info) {
2381 tracing::trace!(
2382 current = ?info,
2383 prev = ?prev,
2384 "current platform header conflicts with previous duplicate header"
2385 );
2386 return Err(Error::MultiplePlatformHeadersWithSameIsolation);
2387 }
2388 }
2389 }
2390 }
2391
2392 if !at_least_one {
2393 Err(Error::NoPlatformHeaders)
2394 } else {
2395 Ok(())
2396 }
2397 }
2398
2399 fn validate_initialization_headers(
2403 revision: IgvmRevision,
2404 initialization_headers: &[IgvmInitializationHeader],
2405 ) -> Result<DirectiveHeaderValidationInfo, Error> {
2406 let mut page_table_masks = 0;
2407 let mut used_vp_idents: Vec<VpIdentifier> = Vec::new();
2408 let mut reloc_regions: HashMap<u32, RangeMap<u64, ()>> = HashMap::new();
2409 let mut page_table_regions = Vec::new();
2410
2411 let mut check_region_overlap =
2412 |compatibility_mask: u32, start: u64, size: u64| -> Result<(), Error> {
2413 for mask in extract_individual_masks(compatibility_mask) {
2414 let regions = match reloc_regions.get_mut(&mask) {
2415 Some(value) => value,
2416 None => {
2417 reloc_regions.insert(mask, RangeMap::new());
2418 reloc_regions.get_mut(&mask).expect("just inserted")
2419 }
2420 };
2421
2422 if !regions.insert(start..=start + size - 1, ()) {
2423 return Err(Error::RelocationRegionsOverlap);
2424 }
2425 }
2426
2427 Ok(())
2428 };
2429
2430 for header in initialization_headers {
2431 header
2433 .validate()
2434 .map_err(Error::InvalidBinaryInitializationHeader)?;
2435
2436 match header {
2444 IgvmInitializationHeader::PageTableRelocationRegion {
2445 compatibility_mask,
2446 gpa,
2447 size,
2448 used_size: _,
2449 vp_index,
2450 vtl,
2451 } => {
2452 if !matches!(revision.arch(), Arch::X64 | Arch::AArch64) {
2453 return Err(Error::InvalidHeaderArch {
2454 arch: revision.arch(),
2455 header_type: "PageTableRelocationRegion".into(),
2456 });
2457 }
2458
2459 if compatibility_mask & page_table_masks != 0 {
2461 return Err(Error::MultiplePageTableRelocationHeaders);
2462 }
2463 page_table_masks |= compatibility_mask;
2464
2465 check_region_overlap(*compatibility_mask, *gpa, *size)?;
2466
2467 used_vp_idents.push(VpIdentifier {
2468 compatibility_mask: *compatibility_mask,
2469 vp_index: *vp_index,
2470 vtl: *vtl,
2471 });
2472 page_table_regions.push(PageTableRegion {
2473 compatibility_mask: *compatibility_mask,
2474 gpa: *gpa,
2475 size: *size,
2476 })
2477 }
2478 IgvmInitializationHeader::RelocatableRegion {
2479 compatibility_mask,
2480 relocation_alignment: _,
2481 relocation_region_gpa,
2482 relocation_region_size,
2483 minimum_relocation_gpa: _,
2484 maximum_relocation_gpa: _,
2485 is_vtl2: _,
2486 apply_rip_offset: _,
2487 apply_gdtr_offset: _,
2488 vp_index,
2489 vtl,
2490 } => {
2491 if !matches!(revision.arch(), Arch::X64 | Arch::AArch64) {
2492 return Err(Error::InvalidHeaderArch {
2493 arch: revision.arch(),
2494 header_type: "RelocatableRegion".into(),
2495 });
2496 }
2497
2498 check_region_overlap(
2499 *compatibility_mask,
2500 *relocation_region_gpa,
2501 *relocation_region_size,
2502 )?;
2503
2504 used_vp_idents.push(VpIdentifier {
2505 compatibility_mask: *compatibility_mask,
2506 vp_index: *vp_index,
2507 vtl: *vtl,
2508 })
2509 }
2510 _ => {}
2512 }
2513 }
2514
2515 Ok(DirectiveHeaderValidationInfo {
2516 used_vp_idents,
2517 page_table_regions,
2518 })
2519 }
2520
2521 fn validate_directive_headers(
2530 revision: IgvmRevision,
2531 directive_headers: &[IgvmDirectiveHeader],
2532 mut validation_info: DirectiveHeaderValidationInfo,
2533 ) -> Result<(), Error> {
2534 #[derive(PartialEq, Eq)]
2535 enum ParameterAreaState {
2536 Allocated,
2537 Inserted,
2538 }
2539 let mut parameter_areas: BTreeMap<u32, ParameterAreaState> = BTreeMap::new();
2540
2541 for header in directive_headers {
2544 header
2545 .validate()
2546 .map_err(Error::InvalidBinaryDirectiveHeader)?;
2547
2548 match header {
2549 IgvmDirectiveHeader::PageData { .. } => {}
2550 IgvmDirectiveHeader::ParameterArea {
2551 parameter_area_index,
2552 ..
2553 } => {
2554 if parameter_areas
2556 .insert(*parameter_area_index, ParameterAreaState::Allocated)
2557 .is_some()
2558 {
2559 return Err(Error::InvalidParameterAreaIndex);
2560 }
2561 }
2562 IgvmDirectiveHeader::VpCount(info)
2563 | IgvmDirectiveHeader::EnvironmentInfo(info)
2564 | IgvmDirectiveHeader::Srat(info)
2565 | IgvmDirectiveHeader::Madt(info)
2566 | IgvmDirectiveHeader::Slit(info)
2567 | IgvmDirectiveHeader::Pptt(info)
2568 | IgvmDirectiveHeader::MmioRanges(info)
2569 | IgvmDirectiveHeader::MemoryMap(info)
2570 | IgvmDirectiveHeader::CommandLine(info)
2571 | IgvmDirectiveHeader::DeviceTree(info) => {
2572 match parameter_areas.get(&info.parameter_area_index) {
2573 Some(ParameterAreaState::Allocated) => {}
2574 _ => return Err(Error::InvalidParameterAreaIndex),
2575 }
2576 }
2577 IgvmDirectiveHeader::RequiredMemory { .. } => {}
2578 IgvmDirectiveHeader::SnpVpContext { .. } => {
2579 }
2582 IgvmDirectiveHeader::X64NativeVpContext {
2583 compatibility_mask: _,
2584 context: _,
2585 vp_index: _,
2586 } => {
2587 if revision.arch() != Arch::X64 {
2588 return Err(Error::InvalidHeaderArch {
2589 arch: revision.arch(),
2590 header_type: "X64VbsVpContext".into(),
2591 });
2592 }
2593 }
2594 IgvmDirectiveHeader::X64VbsVpContext {
2595 vtl,
2596 registers: _,
2597 compatibility_mask,
2598 } => {
2599 if revision.arch() != Arch::X64 {
2600 return Err(Error::InvalidHeaderArch {
2601 arch: revision.arch(),
2602 header_type: "X64VbsVpContext".into(),
2603 });
2604 }
2605
2606 validation_info.used_vp_idents.retain(|ident| {
2608 !((ident.compatibility_mask & compatibility_mask != 0)
2609 && ident.vp_index == 0
2610 && ident.vtl == *vtl)
2611 })
2612 }
2613 IgvmDirectiveHeader::AArch64VbsVpContext {
2614 vtl,
2615 registers: _,
2616 compatibility_mask,
2617 } => {
2618 if revision.arch() != Arch::AArch64 {
2619 return Err(Error::InvalidHeaderArch {
2620 arch: revision.arch(),
2621 header_type: "AArch64VbsVpContext".into(),
2622 });
2623 }
2624
2625 validation_info.used_vp_idents.retain(|ident| {
2627 !((ident.compatibility_mask & compatibility_mask != 0)
2628 && ident.vp_index == 0
2629 && ident.vtl == *vtl)
2630 })
2631 }
2632 IgvmDirectiveHeader::ParameterInsert(info) => {
2633 match parameter_areas.get_mut(&info.parameter_area_index) {
2634 Some(state) if *state == ParameterAreaState::Allocated => {
2635 *state = ParameterAreaState::Inserted;
2637 }
2638 _ => return Err(Error::InvalidParameterAreaIndex),
2639 }
2640
2641 if validation_info.page_table_regions.iter().any(|region| {
2643 let start = region.gpa;
2644 let end = region.gpa + region.size;
2645 (region.compatibility_mask & info.compatibility_mask != 0)
2646 && info.gpa >= start
2647 && info.gpa < end
2648 }) {
2649 return Err(Error::ParameterInsertInsidePageTableRegion);
2650 }
2651 }
2652 IgvmDirectiveHeader::ErrorRange { .. } => {} IgvmDirectiveHeader::SnpIdBlock { .. } => {} IgvmDirectiveHeader::VbsMeasurement { .. } => {} }
2656 }
2657
2658 if !validation_info.used_vp_idents.is_empty() {
2659 return Err(Error::NoMatchingVpContext);
2660 }
2661
2662 Ok(())
2663 }
2664
2665 pub fn serialize(&self, output: &mut Vec<u8>) -> Result<(), Error> {
2668 IgvmFile::validate_platform_headers(self.revision, self.platform_headers.iter())?;
2669
2670 let mut variable_header_section_size = 0;
2673 for header in self.platform_headers.iter() {
2674 variable_header_section_size += header.header_size();
2675 }
2676 for header in self.initialization_headers.iter() {
2677 variable_header_section_size += header.header_size();
2678 }
2679 for header in self.directive_headers.iter() {
2680 variable_header_section_size += header.header_size();
2681 }
2682
2683 assert_eq!(variable_header_section_size % 8, 0);
2685
2686 let file_data_section_start =
2688 self.revision.fixed_header_size() + variable_header_section_size;
2689
2690 let mut variable_header_binary = Vec::new();
2691
2692 for header in &self.platform_headers {
2694 match header {
2695 IgvmPlatformHeader::SupportedPlatform(platform) => {
2696 let header = IGVM_VHS_VARIABLE_HEADER {
2697 typ: IgvmVariableHeaderType::IGVM_VHT_SUPPORTED_PLATFORM,
2698 length: platform.as_bytes().len() as u32,
2699 };
2700 variable_header_binary.extend_from_slice(header.as_bytes());
2701 variable_header_binary.extend_from_slice(platform.as_bytes());
2702 }
2703 }
2704 }
2705
2706 for header in &self.initialization_headers {
2708 header
2709 .write_binary_header(&mut variable_header_binary)
2710 .map_err(Error::InvalidBinaryDirectiveHeader)?;
2711 assert_eq!(variable_header_binary.len() % 8, 0);
2712 }
2713
2714 let mut file_data = FileDataSerializer::new(file_data_section_start);
2716
2717 for header in &self.directive_headers {
2719 header
2720 .write_binary_header(&mut variable_header_binary, &mut file_data)
2721 .map_err(Error::InvalidBinaryDirectiveHeader)?;
2722 assert_eq!(variable_header_binary.len() % 8, 0);
2725 }
2726
2727 assert_eq!(variable_header_section_size, variable_header_binary.len());
2728
2729 let mut fixed_header = match self.revision {
2730 IgvmRevision::V1 => FixedHeader::V1(IGVM_FIXED_HEADER {
2731 magic: IGVM_MAGIC_VALUE,
2732 format_version: IGVM_FORMAT_VERSION_1,
2733 variable_header_offset: size_of::<IGVM_FIXED_HEADER>() as u32,
2734 variable_header_size: variable_header_binary
2735 .len()
2736 .try_into()
2737 .map_err(|_| Error::VariableHeaderSectionTooLarge)?,
2738 total_file_size: 0,
2739 checksum: 0,
2740 }),
2741 IgvmRevision::V2 { arch, page_size } => FixedHeader::V2(IGVM_FIXED_HEADER_V2 {
2742 magic: IGVM_MAGIC_VALUE,
2743 format_version: IGVM_FORMAT_VERSION_2,
2744 variable_header_offset: size_of::<IGVM_FIXED_HEADER_V2>() as u32,
2745 variable_header_size: variable_header_binary
2746 .len()
2747 .try_into()
2748 .map_err(|_| Error::VariableHeaderSectionTooLarge)?,
2749 total_file_size: 0,
2750 checksum: 0,
2751 architecture: arch.into(),
2752 page_size,
2753 }),
2754 };
2755
2756 let mut file_data = file_data.take();
2757 let total_file_size =
2759 fixed_header.as_bytes().len() + variable_header_binary.len() + file_data.len();
2760
2761 fixed_header.set_total_file_size(
2762 total_file_size
2763 .try_into()
2764 .map_err(|_| Error::TotalFileSizeTooLarge)?,
2765 );
2766
2767 let mut hasher = crc32fast::Hasher::new();
2770 hasher.update(fixed_header.as_bytes());
2771 hasher.update(&variable_header_binary);
2772 let checksum = hasher.finalize();
2773 fixed_header.set_checksum(checksum);
2774
2775 output.extend_from_slice(fixed_header.as_bytes());
2777 output.append(&mut variable_header_binary);
2778 output.append(&mut file_data);
2779
2780 Ok(())
2781 }
2782
2783 pub fn new(
2785 revision: IgvmRevision,
2786 platform_headers: Vec<IgvmPlatformHeader>,
2787 initialization_headers: Vec<IgvmInitializationHeader>,
2788 directive_headers: Vec<IgvmDirectiveHeader>,
2789 ) -> Result<Self, Error> {
2790 if revision.page_size() != PAGE_SIZE_4K {
2792 return Err(Error::UnsupportedPageSize(revision.page_size() as u32));
2793 }
2794
2795 Self::validate_platform_headers(revision, platform_headers.iter())?;
2796 let validation_info =
2797 Self::validate_initialization_headers(revision, &initialization_headers)?;
2798 Self::validate_directive_headers(revision, &directive_headers, validation_info)?;
2799
2800 Ok(Self {
2801 revision,
2802 platform_headers,
2803 initialization_headers,
2804 directive_headers,
2805 })
2806 }
2807
2808 pub fn new_from_binary(
2812 file: &[u8],
2813 isolation_filter: Option<IsolationType>,
2814 ) -> Result<Self, Error> {
2815 let total_size = file.len();
2816
2817 let mut fixed_header = FixedHeader::V1(
2819 IGVM_FIXED_HEADER::read_from_prefix(file)
2820 .map_err(|_| Error::InvalidFixedHeader)?
2821 .0, );
2823
2824 if fixed_header.magic() != IGVM_MAGIC_VALUE {
2825 return Err(Error::InvalidFixedHeader);
2826 }
2827
2828 let revision = match fixed_header.format_version() {
2829 IGVM_FORMAT_VERSION_1 => IgvmRevision::V1,
2830 IGVM_FORMAT_VERSION_2 => {
2831 let v2 = IGVM_FIXED_HEADER_V2::read_from_prefix(file)
2832 .map_err(|_| Error::InvalidFixedHeader)?
2833 .0; let arch = match v2.architecture {
2836 IgvmArchitecture::X64 => Arch::X64,
2837 IgvmArchitecture::AARCH64 => Arch::AArch64,
2838 _ => return Err(Error::InvalidFixedHeader),
2839 };
2840
2841 if v2.page_size != PAGE_SIZE_4K as u32 {
2842 return Err(Error::UnsupportedPageSize(v2.page_size));
2843 }
2844
2845 let revision = IgvmRevision::V2 {
2846 arch,
2847 page_size: v2.page_size,
2848 };
2849
2850 fixed_header = FixedHeader::V2(v2);
2851 revision
2852 }
2853 _ => return Err(Error::InvalidFixedHeader),
2854 };
2855
2856 if fixed_header.total_file_size() as usize != total_size {
2857 return Err(Error::InvalidFixedHeader);
2858 }
2859
2860 let variable_header_start = fixed_header.variable_header_offset() as usize;
2861
2862 if variable_header_start >= total_size {
2863 return Err(Error::InvalidFixedHeader);
2864 }
2865
2866 let file_data_start =
2867 fixed_header.variable_header_offset() + fixed_header.variable_header_size();
2868
2869 if file_data_start as usize >= total_size {
2870 return Err(Error::InvalidFixedHeader);
2871 }
2872
2873 let (mut variable_headers, file_data) =
2875 file[variable_header_start..].split_at(fixed_header.variable_header_size() as usize);
2876
2877 let mut fixed_header_calculate_checksum = fixed_header.clone();
2881 fixed_header_calculate_checksum.set_checksum(0);
2882 let mut hasher = crc32fast::Hasher::new();
2883 hasher.update(fixed_header_calculate_checksum.as_bytes());
2884 hasher.update(variable_headers);
2885 let checksum = hasher.finalize();
2886
2887 if fixed_header.checksum() != checksum {
2888 return Err(Error::InvalidChecksum {
2889 expected: checksum,
2890 header_value: fixed_header.checksum(),
2891 });
2892 }
2893
2894 #[derive(PartialEq, Eq)]
2895 enum VariableHeaderParsingStage {
2896 Platform,
2897 Initialization,
2898 Directive,
2899 }
2900
2901 let mut parsing_stage = VariableHeaderParsingStage::Platform;
2902 let mut platform_headers = Vec::new();
2903 let mut initialization_headers = Vec::new();
2904 let mut directive_headers = Vec::new();
2905 let mut filter_mask: u32 = 0xFFFFFFFF;
2906 let isolation_filter: Option<IgvmPlatformType> = isolation_filter.map(|typ| typ.into());
2907 let mut mask_map: HashMap<u32, IgvmPlatformType> = HashMap::new();
2908
2909 while !variable_headers.is_empty() {
2910 match IGVM_VHS_VARIABLE_HEADER::read_from_prefix(variable_headers)
2912 .ok()
2913 .map(|h| h.0)
2914 {
2915 Some(header) if IGVM_VHT_RANGE_PLATFORM.contains(&header.typ.0) => {
2916 if parsing_stage != VariableHeaderParsingStage::Platform {
2917 return Err(Error::InvalidBinaryVariableHeaderSection);
2919 }
2920
2921 let (header, new_slice) =
2922 IgvmPlatformHeader::new_from_binary_split(variable_headers)
2923 .map_err(Error::InvalidBinaryPlatformHeader)?;
2924
2925 match header {
2926 IgvmPlatformHeader::SupportedPlatform(info) => {
2927 if let Some(filter_type) = isolation_filter {
2929 if filter_type == info.platform_type {
2930 filter_mask = info.compatibility_mask;
2931 }
2932 }
2933
2934 mask_map.insert(info.compatibility_mask, info.platform_type);
2935 }
2936 }
2937
2938 platform_headers.push(header);
2939 variable_headers = new_slice;
2940 }
2941 Some(header) if IGVM_VHT_RANGE_INIT.contains(&header.typ.0) => {
2942 match parsing_stage {
2943 VariableHeaderParsingStage::Platform => {
2944 parsing_stage = VariableHeaderParsingStage::Initialization
2945 }
2946 VariableHeaderParsingStage::Initialization => {}
2947 VariableHeaderParsingStage::Directive => {
2948 return Err(Error::InvalidBinaryVariableHeaderSection);
2949 }
2950 }
2951
2952 let (header, new_slice) =
2953 IgvmInitializationHeader::new_from_binary_split(variable_headers)
2954 .map_err(Error::InvalidBinaryInitializationHeader)?;
2955
2956 variable_headers = new_slice;
2957
2958 if let Some(mask) = header.compatibility_mask() {
2959 if mask & filter_mask == 0 {
2960 continue;
2962 }
2963 }
2964
2965 initialization_headers.push(header);
2966 }
2967 Some(header) if IGVM_VHT_RANGE_DIRECTIVE.contains(&header.typ.0) => {
2968 match parsing_stage {
2969 VariableHeaderParsingStage::Platform
2970 | VariableHeaderParsingStage::Initialization => {
2971 parsing_stage = VariableHeaderParsingStage::Directive
2972 }
2973 VariableHeaderParsingStage::Directive => {}
2974 }
2975
2976 let compatibility_mask_to_platforms =
2977 |mask: u32| -> Option<IgvmPlatformType> { mask_map.get(&mask).copied() };
2978
2979 let (header, new_slice) = IgvmDirectiveHeader::new_from_binary_split(
2980 revision,
2981 variable_headers,
2982 file_data,
2983 file_data_start,
2984 compatibility_mask_to_platforms,
2985 )
2986 .map_err(Error::InvalidBinaryDirectiveHeader)?;
2987
2988 variable_headers = new_slice;
2989
2990 if let Some(mask) = header.compatibility_mask() {
2991 if mask & filter_mask == 0 {
2992 continue;
2994 }
2995 }
2996
2997 directive_headers.push(header);
2998 }
2999 other => {
3000 eprintln!("invalid header: {:?}", Some(other));
3001 return Err(Error::InvalidBinaryVariableHeaderSection);
3002 }
3003 }
3004 }
3005
3006 IgvmFile::new(
3007 revision,
3008 platform_headers,
3009 initialization_headers,
3010 directive_headers,
3011 )
3012 }
3013
3014 pub fn platforms(&self) -> &[IgvmPlatformHeader] {
3016 self.platform_headers.as_slice()
3017 }
3018
3019 pub fn initializations(&self) -> &[IgvmInitializationHeader] {
3021 self.initialization_headers.as_slice()
3022 }
3023
3024 pub fn directives(&self) -> &[IgvmDirectiveHeader] {
3026 self.directive_headers.as_slice()
3027 }
3028
3029 pub fn relocations(
3033 &self,
3034 compatibility_mask: u32,
3035 ) -> (
3036 Option<Vec<IgvmRelocatableRegion>>,
3037 Option<PageTableRelocationBuilder>,
3038 ) {
3039 let mut regions = Vec::new();
3040 let mut page_table_fixup = None;
3041
3042 for header in &self.initialization_headers {
3043 if let Some(mask) = header.compatibility_mask() {
3044 if mask & compatibility_mask != compatibility_mask {
3045 continue;
3046 }
3047 }
3048
3049 match header {
3050 IgvmInitializationHeader::RelocatableRegion {
3051 compatibility_mask: _,
3052 relocation_alignment,
3053 relocation_region_gpa,
3054 relocation_region_size,
3055 minimum_relocation_gpa,
3056 maximum_relocation_gpa,
3057 is_vtl2,
3058 apply_rip_offset,
3059 apply_gdtr_offset,
3060 vp_index,
3061 vtl,
3062 } => {
3063 regions.push(IgvmRelocatableRegion {
3064 base_gpa: *relocation_region_gpa,
3065 relocation_alignment: *relocation_alignment,
3066 size: *relocation_region_size,
3067 minimum_relocation_gpa: *minimum_relocation_gpa,
3068 maximum_relocation_gpa: *maximum_relocation_gpa,
3069 is_vtl2: *is_vtl2,
3070 apply_rip_offset: *apply_rip_offset,
3071 apply_gdtr_offset: *apply_gdtr_offset,
3072 vp_index: *vp_index,
3073 vtl: *vtl,
3074 });
3075 }
3076 IgvmInitializationHeader::PageTableRelocationRegion {
3077 compatibility_mask: _,
3078 gpa,
3079 size,
3080 used_size,
3081 vp_index,
3082 vtl,
3083 } => {
3084 assert!(page_table_fixup.is_none());
3085 page_table_fixup = Some(PageTableRelocationBuilder::new(
3086 *gpa, *size, *used_size, *vp_index, *vtl,
3087 ))
3088 }
3089 _ => {}
3090 }
3091 }
3092
3093 let regions = if !regions.is_empty() {
3094 Some(regions)
3095 } else {
3096 None
3097 };
3098 (regions, page_table_fixup)
3099 }
3100
3101 fn merge_dedup_directives(
3112 &mut self,
3113 other_directives: Vec<IgvmDirectiveHeader>,
3114 ) -> Result<(), Error> {
3115 let mut insert_index = 0;
3116 'outer: for other_header in other_directives {
3117 for (index, header) in self.directive_headers[insert_index..]
3120 .iter_mut()
3121 .enumerate()
3122 .rev()
3123 {
3124 if header.equivalent(&other_header) {
3125 match (
3126 header.compatibility_mask_mut(),
3127 other_header.compatibility_mask(),
3128 ) {
3129 (Some(header_mask), Some(other_header_mask)) => {
3130 debug_assert!(*header_mask & other_header_mask == 0);
3131 *header_mask |= other_header_mask
3132 }
3133 (None, None) => {}
3134 _ => unreachable!(),
3135 }
3136 insert_index += index + 1;
3140 continue 'outer;
3141 }
3142 }
3143 self.directive_headers.insert(insert_index, other_header);
3146 insert_index += 1;
3147 }
3148
3149 Ok(())
3150 }
3151
3152 fn merge_internal(&mut self, mut other: IgvmFile, dedup_directives: bool) -> Result<(), Error> {
3158 #[cfg(debug_assertions)]
3161 {
3162 debug_assert!(Self::validate_platform_headers(
3163 self.revision,
3164 self.platform_headers.iter()
3165 )
3166 .is_ok());
3167 debug_assert!(Self::validate_platform_headers(
3168 other.revision,
3169 other.platform_headers.iter()
3170 )
3171 .is_ok());
3172 let self_info =
3173 Self::validate_initialization_headers(self.revision, &self.initialization_headers)
3174 .expect("valid file");
3175 let other_info = Self::validate_initialization_headers(
3176 other.revision,
3177 &other.initialization_headers,
3178 )
3179 .expect("valid file");
3180 debug_assert!(Self::validate_directive_headers(
3181 self.revision,
3182 &self.directive_headers,
3183 self_info
3184 )
3185 .is_ok());
3186 debug_assert!(Self::validate_directive_headers(
3187 other.revision,
3188 &other.directive_headers,
3189 other_info
3190 )
3191 .is_ok());
3192 }
3193
3194 if self.revision != other.revision {
3195 return Err(Error::MergeRevision);
3196 }
3197
3198 Self::validate_platform_headers(
3199 self.revision,
3200 self.platform_headers
3201 .iter()
3202 .chain(other.platform_headers.iter()),
3203 )?;
3204
3205 let mut used_compatibility_masks =
3209 self.platform_headers
3210 .iter()
3211 .fold(0, |mask, header| match header {
3212 IgvmPlatformHeader::SupportedPlatform(platform) => {
3213 mask | platform.compatibility_mask
3214 }
3215 });
3216 let mut fixup_masks_map = HashMap::new();
3217 for header in &other.platform_headers {
3218 match header {
3219 IgvmPlatformHeader::SupportedPlatform(platform) => {
3220 if platform.compatibility_mask & used_compatibility_masks != 0 {
3221 let free_bit = used_compatibility_masks.trailing_ones();
3223
3224 if free_bit > 32 {
3225 return Err(Error::NoFreeCompatibilityMasks);
3231 }
3232
3233 let new_mask = 1u32 << free_bit;
3234 used_compatibility_masks |= new_mask;
3235
3236 assert!(fixup_masks_map
3237 .insert(platform.compatibility_mask, new_mask)
3238 .is_none());
3239 }
3240 }
3241 }
3242 }
3243
3244 let fixup_masks_map = fixup_masks_map;
3245 let fixup_mask_all_bits = fixup_masks_map.iter().fold(0, |mask, entry| mask | entry.0);
3246
3247 let fixup_mask = |mask: &mut u32| {
3248 let mut bits_to_be_fixed: u32 = *mask & fixup_mask_all_bits;
3249 while bits_to_be_fixed != 0 {
3250 let old_mask = 1 << bits_to_be_fixed.trailing_zeros();
3251 let new_mask = fixup_masks_map
3252 .get(&old_mask)
3253 .expect("old_mask should always be present");
3254 *mask = (!old_mask & *mask) | new_mask;
3255 bits_to_be_fixed &= !old_mask;
3256 }
3257 };
3258
3259 let mut used_parameter_indices = BTreeSet::new();
3262 let mut fixup_parameter_index_map: BTreeMap<u32, u32> = BTreeMap::new();
3263 for header in &self.directive_headers {
3264 use IgvmDirectiveHeader::*;
3265 if let ParameterArea {
3266 parameter_area_index,
3267 ..
3268 } = header
3269 {
3270 assert!(
3273 used_parameter_indices.insert(*parameter_area_index),
3274 "invalid igvm file, parameter index used twice"
3275 );
3276 }
3277 }
3278
3279 let allocate_new_parameter_index =
3285 |used_parameter_indices: &mut BTreeSet<u32>| -> Result<u32, Error> {
3286 let mut new_index: u32 = used_parameter_indices
3288 .len()
3289 .try_into()
3290 .map_err(|_| Error::InvalidParameterAreaIndex)?;
3291 for (index, val) in used_parameter_indices.iter().enumerate() {
3292 let index = index as u32;
3293 if index != *val {
3294 new_index = index;
3295 break;
3296 }
3297 }
3298 assert!(used_parameter_indices.insert(new_index));
3299 Ok(new_index)
3300 };
3301
3302 let fixup_parameter_index =
3303 |index: &mut u32, fixup_parameter_index_map: &BTreeMap<u32, u32>| {
3304 if let Some(new_index) = fixup_parameter_index_map.get(index) {
3307 *index = *new_index;
3308 }
3309 };
3310
3311 for header in &mut other.platform_headers {
3313 match header {
3314 IgvmPlatformHeader::SupportedPlatform(platform) => {
3315 fixup_mask(&mut platform.compatibility_mask)
3316 }
3317 }
3318 }
3319
3320 for header in &mut other.initialization_headers {
3321 match header {
3322 IgvmInitializationHeader::GuestPolicy {
3323 policy: _,
3324 compatibility_mask,
3325 } => fixup_mask(compatibility_mask),
3326 IgvmInitializationHeader::RelocatableRegion {
3327 compatibility_mask, ..
3328 } => fixup_mask(compatibility_mask),
3329 IgvmInitializationHeader::PageTableRelocationRegion {
3330 compatibility_mask, ..
3331 } => fixup_mask(compatibility_mask),
3332 }
3333 }
3334
3335 for header in &mut other.directive_headers {
3338 use IgvmDirectiveHeader::*;
3339
3340 if let Some(mask) = header.compatibility_mask_mut() {
3341 fixup_mask(mask);
3342 }
3343
3344 match header {
3345 RequiredMemory { .. }
3346 | PageData { .. }
3347 | SnpVpContext { .. }
3348 | X64NativeVpContext { .. }
3349 | ErrorRange { .. }
3350 | SnpIdBlock { .. }
3351 | VbsMeasurement { .. }
3352 | X64VbsVpContext { .. }
3353 | AArch64VbsVpContext { .. } => {}
3354 ParameterArea {
3355 parameter_area_index,
3356 ..
3357 } => {
3358 if used_parameter_indices.contains(parameter_area_index) {
3359 match fixup_parameter_index_map.get(parameter_area_index) {
3364 Some(_) => panic!("igvm file is invalid, parameter index used twice"),
3365 None => {
3366 let new_index =
3367 allocate_new_parameter_index(&mut used_parameter_indices)?;
3368 assert!(fixup_parameter_index_map
3369 .insert(*parameter_area_index, new_index)
3370 .is_none());
3371 *parameter_area_index = new_index;
3372 }
3373 }
3374 }
3375 }
3376 VpCount(info)
3377 | EnvironmentInfo(info)
3378 | Srat(info)
3379 | Madt(info)
3380 | Slit(info)
3381 | Pptt(info)
3382 | MmioRanges(info)
3383 | MemoryMap(info)
3384 | CommandLine(info)
3385 | DeviceTree(info) => {
3386 fixup_parameter_index(
3387 &mut info.parameter_area_index,
3388 &fixup_parameter_index_map,
3389 );
3390 }
3391 ParameterInsert(insert) => {
3392 fixup_parameter_index(
3393 &mut insert.parameter_area_index,
3394 &fixup_parameter_index_map,
3395 );
3396 }
3397 }
3398 }
3399
3400 self.platform_headers.append(&mut other.platform_headers);
3402 self.initialization_headers
3403 .append(&mut other.initialization_headers);
3404
3405 if dedup_directives {
3407 self.merge_dedup_directives(other.directive_headers)?;
3408 } else {
3409 self.directive_headers.append(&mut other.directive_headers);
3410 }
3411
3412 Ok(())
3413 }
3414
3415 pub fn merge(&mut self, other: IgvmFile) -> Result<(), Error> {
3433 self.merge_internal(other, true)
3434 }
3435
3436 pub fn merge_simple(&mut self, other: IgvmFile) -> Result<(), Error> {
3443 self.merge_internal(other, false)
3444 }
3445}
3446
3447#[cfg(test)]
3448mod tests {
3449 use super::*;
3450 use crate::hv_defs::HvArm64RegisterName;
3451 use crate::hv_defs::HvRegisterValue;
3452
3453 fn new_platform(
3454 compatibility_mask: u32,
3455 platform_type: IgvmPlatformType,
3456 ) -> IgvmPlatformHeader {
3457 IgvmPlatformHeader::SupportedPlatform(IGVM_VHS_SUPPORTED_PLATFORM {
3458 compatibility_mask,
3459 highest_vtl: 0,
3460 platform_type,
3461 platform_version: 1,
3462 shared_gpa_boundary: 0,
3463 })
3464 }
3465
3466 fn new_page_data(page: u64, compatibility_mask: u32, data: &[u8]) -> IgvmDirectiveHeader {
3467 IgvmDirectiveHeader::PageData {
3468 gpa: page * PAGE_SIZE_4K,
3469 compatibility_mask,
3470 flags: IgvmPageDataFlags::new(),
3471 data_type: IgvmPageDataType::NORMAL,
3472 data: data.to_vec(),
3473 }
3474 }
3475
3476 fn assert_igvm_equal(a: &IgvmFile, b: &IgvmFile) {
3477 assert_eq!(a.revision, b.revision);
3478
3479 for (a, b) in a.platform_headers.iter().zip(b.platform_headers.iter()) {
3480 assert_eq!(a, b);
3481 }
3482
3483 for (a, b) in a
3484 .initialization_headers
3485 .iter()
3486 .zip(b.initialization_headers.iter())
3487 {
3488 assert_eq!(a, b);
3489 }
3490
3491 for (a, b) in a.directive_headers.iter().zip(b.directive_headers.iter()) {
3492 assert_eq!(a, b);
3493 }
3494 }
3495
3496 fn new_parameter_area(index: u32) -> IgvmDirectiveHeader {
3497 IgvmDirectiveHeader::ParameterArea {
3498 number_of_bytes: 4096,
3499 parameter_area_index: index,
3500 initial_data: vec![],
3501 }
3502 }
3503
3504 fn new_parameter_usage(index: u32) -> IgvmDirectiveHeader {
3505 IgvmDirectiveHeader::VpCount(IGVM_VHS_PARAMETER {
3506 parameter_area_index: index,
3507 byte_offset: 0,
3508 })
3509 }
3510
3511 fn new_parameter_insert(page: u64, index: u32, mask: u32) -> IgvmDirectiveHeader {
3512 IgvmDirectiveHeader::ParameterInsert(IGVM_VHS_PARAMETER_INSERT {
3513 gpa: page * PAGE_SIZE_4K,
3514 parameter_area_index: index,
3515 compatibility_mask: mask,
3516 })
3517 }
3518
3519 mod new_from_binary {
3520 use super::*;
3521 #[test]
3526 fn test_basic() {
3527 let data1 = vec![1; PAGE_SIZE_4K as usize];
3528 let data2 = vec![2; PAGE_SIZE_4K as usize];
3529 let data3 = vec![3; PAGE_SIZE_4K as usize];
3530 let data4 = vec![4; PAGE_SIZE_4K as usize];
3531 let file = IgvmFile {
3532 revision: IgvmRevision::V1,
3533 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3534 initialization_headers: vec![],
3535 directive_headers: vec![
3536 new_page_data(0, 1, &data1),
3537 new_page_data(1, 1, &data2),
3538 new_page_data(2, 1, &data3),
3539 new_page_data(4, 1, &data4),
3540 new_page_data(10, 1, &data1),
3541 new_page_data(11, 1, &data2),
3542 new_page_data(12, 1, &data3),
3543 new_page_data(14, 1, &data4),
3544 new_parameter_area(0),
3545 new_parameter_usage(0),
3546 new_parameter_insert(20, 0, 1),
3547 ],
3548 };
3549 let mut binary_file = Vec::new();
3550 file.serialize(&mut binary_file).unwrap();
3551
3552 let deserialized_binary_file = IgvmFile::new_from_binary(&binary_file, None).unwrap();
3553 assert_igvm_equal(&file, &deserialized_binary_file);
3554 }
3555
3556 #[test]
3557 fn test_basic_v2() {
3558 let data1 = vec![1; PAGE_SIZE_4K as usize];
3559 let data2 = vec![2; PAGE_SIZE_4K as usize];
3560 let data3 = vec![3; PAGE_SIZE_4K as usize];
3561 let data4 = vec![4; PAGE_SIZE_4K as usize];
3562 let file = IgvmFile {
3563 revision: IgvmRevision::V2 {
3564 arch: Arch::X64,
3565 page_size: PAGE_SIZE_4K as u32,
3566 },
3567 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3568 initialization_headers: vec![],
3569 directive_headers: vec![
3570 new_page_data(0, 1, &data1),
3571 new_page_data(1, 1, &data2),
3572 new_page_data(2, 1, &data3),
3573 new_page_data(4, 1, &data4),
3574 new_page_data(10, 1, &data1),
3575 new_page_data(11, 1, &data2),
3576 new_page_data(12, 1, &data3),
3577 new_page_data(14, 1, &data4),
3578 new_parameter_area(0),
3579 new_parameter_usage(0),
3580 new_parameter_insert(20, 0, 1),
3581 IgvmDirectiveHeader::X64VbsVpContext {
3582 vtl: Vtl::Vtl0,
3583 registers: vec![X86Register::R12(0x1234)],
3584 compatibility_mask: 0x1,
3585 },
3586 ],
3587 };
3588 let mut binary_file = Vec::new();
3589 file.serialize(&mut binary_file).unwrap();
3590
3591 let deserialized_binary_file = IgvmFile::new_from_binary(&binary_file, None).unwrap();
3592 assert_igvm_equal(&file, &deserialized_binary_file);
3593 }
3594
3595 #[test]
3596 fn test_basic_v2_aarch64() {
3597 let data1 = vec![1; PAGE_SIZE_4K as usize];
3598 let data2 = vec![2; PAGE_SIZE_4K as usize];
3599 let data3 = vec![3; PAGE_SIZE_4K as usize];
3600 let data4 = vec![4; PAGE_SIZE_4K as usize];
3601 let file = IgvmFile {
3602 revision: IgvmRevision::V2 {
3603 arch: Arch::AArch64,
3604 page_size: PAGE_SIZE_4K as u32,
3605 },
3606 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3607 initialization_headers: vec![],
3608 directive_headers: vec![
3609 new_page_data(0, 1, &data1),
3610 new_page_data(1, 1, &data2),
3611 new_page_data(2, 1, &data3),
3612 new_page_data(4, 1, &data4),
3613 new_page_data(10, 1, &data1),
3614 new_page_data(11, 1, &data2),
3615 new_page_data(12, 1, &data3),
3616 new_page_data(14, 1, &data4),
3617 new_parameter_area(0),
3618 new_parameter_usage(0),
3619 new_parameter_insert(20, 0, 1),
3620 IgvmDirectiveHeader::AArch64VbsVpContext {
3621 vtl: Vtl::Vtl0,
3622 registers: vec![AArch64Register::X0(0x1234)],
3623 compatibility_mask: 0x1,
3624 },
3625 ],
3626 };
3627 let mut binary_file = Vec::new();
3628 file.serialize(&mut binary_file).unwrap();
3629
3630 let deserialized_binary_file = IgvmFile::new_from_binary(&binary_file, None).unwrap();
3631 assert_igvm_equal(&file, &deserialized_binary_file);
3632 }
3633
3634 }
3637
3638 mod merge {
3639 use super::*;
3640
3641 #[test]
3643 fn test_merge_basic() {
3644 let data1 = vec![1; PAGE_SIZE_4K as usize];
3645 let data2 = vec![2; PAGE_SIZE_4K as usize];
3646 let data3 = vec![3; PAGE_SIZE_4K as usize];
3647 let data4 = vec![4; PAGE_SIZE_4K as usize];
3648 let mut a = IgvmFile {
3649 revision: IgvmRevision::V1,
3650 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3651 initialization_headers: vec![],
3652 directive_headers: vec![
3653 new_page_data(0, 1, &data1),
3654 new_page_data(1, 1, &data2),
3655 new_page_data(2, 1, &data3),
3656 new_page_data(4, 1, &data4),
3657 new_page_data(10, 1, &data1),
3658 new_page_data(11, 1, &data2),
3659 new_page_data(12, 1, &data3),
3660 new_page_data(14, 1, &data4),
3661 ],
3662 };
3663 let b = IgvmFile {
3664 revision: IgvmRevision::V1,
3665 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3666 initialization_headers: vec![],
3667 directive_headers: vec![
3668 new_page_data(0, 1, &data1),
3669 new_page_data(1, 1, &data2),
3670 new_page_data(2, 1, &data3),
3671 new_page_data(4, 1, &data4),
3672 new_page_data(20, 1, &data1),
3673 new_page_data(21, 1, &data2),
3674 new_page_data(22, 1, &data3),
3675 new_page_data(24, 1, &data4),
3676 ],
3677 };
3678 let merged = IgvmFile {
3679 revision: IgvmRevision::V1,
3680 platform_headers: vec![
3681 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3682 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3683 ],
3684 initialization_headers: vec![],
3685 directive_headers: vec![
3686 new_page_data(0, 3, &data1),
3687 new_page_data(1, 3, &data2),
3688 new_page_data(2, 3, &data3),
3689 new_page_data(4, 3, &data4),
3690 new_page_data(20, 2, &data1),
3691 new_page_data(21, 2, &data2),
3692 new_page_data(22, 2, &data3),
3693 new_page_data(24, 2, &data4),
3694 new_page_data(10, 1, &data1),
3695 new_page_data(11, 1, &data2),
3696 new_page_data(12, 1, &data3),
3697 new_page_data(14, 1, &data4),
3698 ],
3699 };
3700
3701 a.merge(b).unwrap();
3702 assert_igvm_equal(&a, &merged);
3703 }
3704
3705 #[test]
3706 fn test_merge_simple() {
3707 let data1 = vec![1; PAGE_SIZE_4K as usize];
3708 let data2 = vec![2; PAGE_SIZE_4K as usize];
3709 let data3 = vec![3; PAGE_SIZE_4K as usize];
3710 let data4 = vec![4; PAGE_SIZE_4K as usize];
3711 let mut a = IgvmFile {
3712 revision: IgvmRevision::V1,
3713 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3714 initialization_headers: vec![],
3715 directive_headers: vec![
3716 new_page_data(0, 1, &data1),
3717 new_page_data(1, 1, &data2),
3718 new_page_data(2, 1, &data3),
3719 new_page_data(4, 1, &data4),
3720 new_page_data(10, 1, &data1),
3721 new_page_data(11, 1, &data2),
3722 new_page_data(12, 1, &data3),
3723 new_page_data(14, 1, &data4),
3724 ],
3725 };
3726 let b = IgvmFile {
3727 revision: IgvmRevision::V1,
3728 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3729 initialization_headers: vec![],
3730 directive_headers: vec![
3731 new_page_data(0, 1, &data1),
3732 new_page_data(1, 1, &data2),
3733 new_page_data(2, 1, &data3),
3734 new_page_data(4, 1, &data4),
3735 new_page_data(20, 1, &data1),
3736 new_page_data(21, 1, &data2),
3737 new_page_data(22, 1, &data3),
3738 new_page_data(24, 1, &data4),
3739 ],
3740 };
3741 let merged = IgvmFile {
3742 revision: IgvmRevision::V1,
3743 platform_headers: vec![
3744 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3745 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3746 ],
3747 initialization_headers: vec![],
3748 directive_headers: vec![
3749 new_page_data(0, 1, &data1),
3751 new_page_data(1, 1, &data2),
3752 new_page_data(2, 1, &data3),
3753 new_page_data(4, 1, &data4),
3754 new_page_data(10, 1, &data1),
3755 new_page_data(11, 1, &data2),
3756 new_page_data(12, 1, &data3),
3757 new_page_data(14, 1, &data4),
3758 new_page_data(0, 2, &data1),
3760 new_page_data(1, 2, &data2),
3761 new_page_data(2, 2, &data3),
3762 new_page_data(4, 2, &data4),
3763 new_page_data(20, 2, &data1),
3764 new_page_data(21, 2, &data2),
3765 new_page_data(22, 2, &data3),
3766 new_page_data(24, 2, &data4),
3767 ],
3768 };
3769
3770 a.merge_simple(b).unwrap();
3771 assert_igvm_equal(&a, &merged);
3772 }
3773
3774 #[test]
3776 fn test_multiple_compat_masks() {
3777 let data1 = vec![1; PAGE_SIZE_4K as usize];
3778 let data2 = vec![2; PAGE_SIZE_4K as usize];
3779
3780 let mut a = IgvmFile {
3782 revision: IgvmRevision::V1,
3783 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3784 initialization_headers: vec![],
3785 directive_headers: vec![new_page_data(0, 1, &data1), new_page_data(1, 1, &data2)],
3786 };
3787 let b = IgvmFile {
3788 revision: IgvmRevision::V1,
3789 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3790 initialization_headers: vec![],
3791 directive_headers: vec![new_page_data(0, 1, &data1), new_page_data(1, 1, &data2)],
3792 };
3793 let c = IgvmFile {
3794 revision: IgvmRevision::V1,
3795 platform_headers: vec![new_platform(0x2, IgvmPlatformType::TDX)],
3796 initialization_headers: vec![],
3797 directive_headers: vec![new_page_data(0, 2, &data1), new_page_data(1, 2, &data2)],
3798 };
3799 let merged = IgvmFile {
3800 revision: IgvmRevision::V1,
3801 platform_headers: vec![
3802 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3803 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3804 new_platform(0x4, IgvmPlatformType::TDX),
3805 ],
3806 initialization_headers: vec![],
3807 directive_headers: vec![new_page_data(0, 7, &data1), new_page_data(1, 7, &data2)],
3808 };
3809 a.merge(b).unwrap();
3810 a.merge(c).unwrap();
3811 assert_igvm_equal(&a, &merged);
3812 }
3813
3814 #[test]
3816 fn test_merge_page_data_should_not_merge() {
3817 let data1 = vec![1; PAGE_SIZE_4K as usize];
3818 let data2 = vec![2; PAGE_SIZE_4K as usize];
3819
3820 let mut a = IgvmFile {
3821 revision: IgvmRevision::V1,
3822 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3823 initialization_headers: vec![],
3824 directive_headers: vec![new_page_data(0, 1, &data2), new_page_data(1, 1, &data1)],
3825 };
3826 let b = IgvmFile {
3827 revision: IgvmRevision::V1,
3828 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3829 initialization_headers: vec![],
3830 directive_headers: vec![new_page_data(0, 1, &data1), new_page_data(1, 1, &data2)],
3831 };
3832 let merged = IgvmFile {
3833 revision: IgvmRevision::V1,
3834 platform_headers: vec![
3835 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3836 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3837 ],
3838 initialization_headers: vec![],
3839 directive_headers: vec![
3840 new_page_data(0, 2, &data1),
3841 new_page_data(1, 2, &data2),
3842 new_page_data(0, 1, &data2),
3843 new_page_data(1, 1, &data1),
3844 ],
3845 };
3846
3847 a.merge(b).unwrap();
3848 assert_igvm_equal(&a, &merged);
3849 }
3850
3851 #[test]
3852 fn test_merge_stable_ordering() {
3853 let data1 = vec![1; PAGE_SIZE_4K as usize];
3857 let data2 = vec![2; PAGE_SIZE_4K as usize];
3858 let data3 = vec![3; PAGE_SIZE_4K as usize];
3859 let data4 = vec![4; PAGE_SIZE_4K as usize];
3860 let mut a = IgvmFile {
3861 revision: IgvmRevision::V1,
3862 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3863 initialization_headers: vec![],
3864 directive_headers: vec![
3865 new_page_data(0, 1, &data1),
3866 new_page_data(1, 1, &data2),
3867 new_page_data(2, 1, &data3),
3868 new_page_data(4, 1, &data4),
3869 new_page_data(10, 1, &data1),
3870 new_page_data(11, 1, &data2),
3871 new_page_data(12, 1, &data3),
3872 new_page_data(14, 1, &data4),
3873 ],
3874 };
3875 let b = IgvmFile {
3876 revision: IgvmRevision::V1,
3877 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3878 initialization_headers: vec![],
3879 directive_headers: vec![
3880 new_page_data(0, 1, &data1),
3881 new_page_data(14, 1, &data4),
3882 new_page_data(1, 1, &data2),
3883 new_page_data(2, 1, &data3),
3884 new_page_data(4, 1, &data4),
3885 new_page_data(10, 1, &data1),
3886 new_page_data(11, 1, &data2),
3887 new_page_data(12, 1, &data3),
3888 ],
3889 };
3890 let merged = IgvmFile {
3891 revision: IgvmRevision::V1,
3892 platform_headers: vec![
3893 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3894 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3895 ],
3896 initialization_headers: vec![],
3897 directive_headers: vec![
3898 new_page_data(0, 3, &data1),
3899 new_page_data(1, 1, &data2),
3900 new_page_data(2, 1, &data3),
3901 new_page_data(4, 1, &data4),
3902 new_page_data(10, 1, &data1),
3903 new_page_data(11, 1, &data2),
3904 new_page_data(12, 1, &data3),
3905 new_page_data(14, 3, &data4),
3906 new_page_data(1, 2, &data2),
3907 new_page_data(2, 2, &data3),
3908 new_page_data(4, 2, &data4),
3909 new_page_data(10, 2, &data1),
3910 new_page_data(11, 2, &data2),
3911 new_page_data(12, 2, &data3),
3912 ],
3913 };
3914 a.merge(b).unwrap();
3915 assert_igvm_equal(&a, &merged);
3916 }
3917
3918 #[test]
3919 fn test_merge_parameter_areas() {
3920 let mut a = IgvmFile {
3927 revision: IgvmRevision::V1,
3928 platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
3929 initialization_headers: vec![],
3930 directive_headers: vec![
3931 new_parameter_area(1),
3932 new_parameter_area(2),
3933 new_parameter_area(7),
3934 new_parameter_usage(1),
3935 new_parameter_usage(2),
3936 new_parameter_usage(7),
3937 new_parameter_insert(1, 1, 1),
3938 new_parameter_insert(2, 2, 1),
3939 new_parameter_insert(10, 7, 1),
3940 ],
3941 };
3942
3943 let b = IgvmFile {
3944 revision: IgvmRevision::V1,
3945 platform_headers: vec![new_platform(0x1, IgvmPlatformType::SEV_SNP)],
3946 initialization_headers: vec![],
3947 directive_headers: vec![
3948 new_parameter_area(1),
3949 new_parameter_area(2),
3950 new_parameter_area(10),
3951 new_parameter_usage(1),
3952 new_parameter_usage(2),
3953 new_parameter_usage(10),
3954 new_parameter_insert(1, 1, 1),
3955 new_parameter_insert(4, 2, 1),
3956 new_parameter_insert(12, 10, 1),
3957 ],
3958 };
3959
3960 let merged = IgvmFile {
3961 revision: IgvmRevision::V1,
3962 platform_headers: vec![
3963 new_platform(0x1, IgvmPlatformType::VSM_ISOLATION),
3964 new_platform(0x2, IgvmPlatformType::SEV_SNP),
3965 ],
3966 initialization_headers: vec![],
3967 directive_headers: vec![
3968 new_parameter_area(0),
3969 new_parameter_area(3),
3970 new_parameter_area(10),
3971 new_parameter_usage(0),
3972 new_parameter_usage(3),
3973 new_parameter_usage(10),
3974 new_parameter_insert(1, 0, 2),
3975 new_parameter_insert(4, 3, 2),
3976 new_parameter_insert(12, 10, 2),
3977 new_parameter_area(1),
3978 new_parameter_area(2),
3979 new_parameter_area(7),
3980 new_parameter_usage(1),
3981 new_parameter_usage(2),
3982 new_parameter_usage(7),
3983 new_parameter_insert(1, 1, 1),
3984 new_parameter_insert(2, 2, 1),
3985 new_parameter_insert(10, 7, 1),
3986 ],
3987 };
3988
3989 a.merge(b).unwrap();
3990 assert_igvm_equal(&a, &merged);
3991 }
3992 }
3993
3994 fn test_variable_header<T: IntoBytes + Immutable + KnownLayout>(
4002 revision: IgvmRevision,
4003 header: IgvmDirectiveHeader,
4004 file_data_offset: u32,
4005 header_type: IgvmVariableHeaderType,
4006 expected_variable_binary_header: T,
4007 expected_file_data: Option<Vec<u8>>,
4008 platform_to_report: Option<IgvmPlatformType>,
4009 ) {
4010 let mut binary_header = Vec::new();
4011 let mut file_data = FileDataSerializer::new(file_data_offset as usize);
4012
4013 header
4014 .write_binary_header(&mut binary_header, &mut file_data)
4015 .unwrap();
4016
4017 let file_data = file_data.take();
4018
4019 let common_header = IGVM_VHS_VARIABLE_HEADER::read_from_prefix(&binary_header[..])
4020 .expect("variable header must be present")
4021 .0;
4022
4023 assert_eq!(common_header.typ, header_type);
4024 assert_eq!(
4025 align_8(common_header.length as usize),
4026 size_of_val(&expected_variable_binary_header)
4027 );
4028 assert_eq!(
4029 &binary_header[size_of_val(&common_header)..],
4030 expected_variable_binary_header.as_bytes()
4031 );
4032
4033 match &expected_file_data {
4034 Some(data) => assert_eq!(data, &file_data),
4035 None => assert!(file_data.is_empty()),
4036 }
4037
4038 let (reserialized_header, remaining) = IgvmDirectiveHeader::new_from_binary_split(
4039 revision,
4040 &binary_header,
4041 &file_data,
4042 file_data_offset,
4043 |_mask| platform_to_report,
4044 )
4045 .unwrap();
4046 assert!(remaining.is_empty());
4047
4048 match (&header, &reserialized_header) {
4050 (
4051 IgvmDirectiveHeader::PageData {
4052 gpa: a_gpa,
4053 flags: a_flags,
4054 data_type: a_data_type,
4055 data: a_data,
4056 compatibility_mask: a_compmask,
4057 },
4058 IgvmDirectiveHeader::PageData {
4059 gpa: b_gpa,
4060 flags: b_flags,
4061 data_type: b_data_type,
4062 data: b_data,
4063 compatibility_mask: b_compmask,
4064 },
4065 ) => {
4066 assert!(
4067 a_gpa == b_gpa
4068 && a_flags == b_flags
4069 && a_data_type == b_data_type
4070 && a_compmask == b_compmask
4071 );
4072
4073 for i in 0..b_data.len() {
4075 if i < a_data.len() {
4076 assert_eq!(a_data[i], b_data[i]);
4077 } else {
4078 assert_eq!(0, b_data[i]);
4079 }
4080 }
4081 }
4082 (
4083 IgvmDirectiveHeader::ParameterArea {
4084 number_of_bytes: a_number_of_bytes,
4085 parameter_area_index: a_parameter_area_index,
4086 initial_data: a_initial_data,
4087 },
4088 IgvmDirectiveHeader::ParameterArea {
4089 number_of_bytes: b_number_of_bytes,
4090 parameter_area_index: b_parameter_area_index,
4091 initial_data: b_initial_data,
4092 },
4093 ) => {
4094 assert!(
4095 a_number_of_bytes == b_number_of_bytes
4096 && a_parameter_area_index == b_parameter_area_index
4097 );
4098
4099 for i in 0..b_initial_data.len() {
4101 if i < a_initial_data.len() {
4102 assert_eq!(a_initial_data[i], b_initial_data[i]);
4103 } else {
4104 assert_eq!(0, b_initial_data[i]);
4105 }
4106 }
4107 }
4108 _ => assert_eq!(header, reserialized_header),
4109 }
4110 }
4111
4112 #[test]
4114 fn test_page_data() {
4115 let gpa = 0x12 * PAGE_SIZE_4K;
4116 let file_data_offset = 0x12340;
4117
4118 let header = IgvmDirectiveHeader::PageData {
4120 gpa,
4121 compatibility_mask: 0,
4122 flags: IgvmPageDataFlags::new(),
4123 data_type: IgvmPageDataType::NORMAL,
4124 data: vec![],
4125 };
4126 let expected_header = IGVM_VHS_PAGE_DATA {
4127 gpa,
4128 ..FromZeros::new_zeroed()
4129 };
4130 test_variable_header(
4131 IgvmRevision::V1,
4132 header,
4133 file_data_offset,
4134 IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA,
4135 expected_header,
4136 None,
4137 None,
4138 );
4139
4140 let mut data = vec![1, 2, 3, 4, 5, 4, 3, 2, 1];
4142 let header = IgvmDirectiveHeader::PageData {
4143 gpa,
4144 compatibility_mask: 0,
4145 flags: IgvmPageDataFlags::new(),
4146 data_type: IgvmPageDataType::NORMAL,
4147 data: data.clone(),
4148 };
4149 let expected_header = IGVM_VHS_PAGE_DATA {
4150 gpa,
4151 file_offset: file_data_offset,
4152 ..FromZeros::new_zeroed()
4153 };
4154 data.resize(PAGE_SIZE_4K as usize, 0);
4155 let expected_file_data = Some(data);
4156 test_variable_header(
4157 IgvmRevision::V1,
4158 header,
4159 file_data_offset,
4160 IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA,
4161 expected_header,
4162 expected_file_data,
4163 None,
4164 );
4165
4166 let data: Vec<u8> = (0..PAGE_SIZE_4K).map(|x| (x % 255) as u8).collect();
4168 let header = IgvmDirectiveHeader::PageData {
4169 gpa,
4170 compatibility_mask: 0,
4171 flags: IgvmPageDataFlags::new(),
4172 data_type: IgvmPageDataType::NORMAL,
4173 data: data.clone(),
4174 };
4175 let expected_header = IGVM_VHS_PAGE_DATA {
4176 gpa,
4177 file_offset: file_data_offset,
4178 ..FromZeros::new_zeroed()
4179 };
4180 let expected_file_data = Some(data);
4181 test_variable_header(
4182 IgvmRevision::V1,
4183 header,
4184 file_data_offset,
4185 IgvmVariableHeaderType::IGVM_VHT_PAGE_DATA,
4186 expected_header,
4187 expected_file_data,
4188 None,
4189 );
4190 }
4191
4192 #[test]
4193 fn test_page_data_dedup() {
4194 let gpa = 0x12 * PAGE_SIZE_4K;
4196 let file_data_offset = 0x12340;
4197 let data = vec![1, 2, 3, 4, 5, 4, 3, 2, 1];
4198 let mut file_data = FileDataSerializer::new(file_data_offset);
4199
4200 let header = IgvmDirectiveHeader::PageData {
4201 gpa,
4202 compatibility_mask: 0,
4203 flags: IgvmPageDataFlags::new(),
4204 data_type: IgvmPageDataType::NORMAL,
4205 data: data.clone(),
4206 };
4207
4208 let mut first = Vec::new();
4209 let mut second = Vec::new();
4210 let mut third = Vec::new();
4211
4212 header
4213 .write_binary_header(&mut first, &mut file_data)
4214 .unwrap();
4215 header
4216 .write_binary_header(&mut second, &mut file_data)
4217 .unwrap();
4218 header
4219 .write_binary_header(&mut third, &mut file_data)
4220 .unwrap();
4221
4222 let mut different = Vec::new();
4223 let header = IgvmDirectiveHeader::PageData {
4224 gpa,
4225 compatibility_mask: 0,
4226 flags: IgvmPageDataFlags::new(),
4227 data_type: IgvmPageDataType::NORMAL,
4228 data: vec![5, 5, 5, 5, 5],
4229 };
4230 header
4231 .write_binary_header(&mut different, &mut file_data)
4232 .unwrap();
4233
4234 let read_raw_header = |data: &[u8]| {
4235 let (_, data) = data.split_at(size_of::<IGVM_VHS_VARIABLE_HEADER>());
4236 IGVM_VHS_PAGE_DATA::read_from_prefix(data).unwrap().0
4237 };
4238
4239 let first = read_raw_header(&first);
4240 let second = read_raw_header(&second);
4241 let third = read_raw_header(&third);
4242 let different = read_raw_header(&different);
4243
4244 assert_eq!(first.file_offset, second.file_offset);
4245 assert_eq!(second.file_offset, third.file_offset);
4246 assert!(different.file_offset != first.file_offset);
4247 assert_eq!(file_data.file_data_map.len(), 2);
4248 }
4249
4250 #[test]
4251 fn test_page_data_over_4k() {
4252 let size = PAGE_SIZE_4K as usize + 1;
4254 let header = IgvmDirectiveHeader::PageData {
4255 gpa: 0,
4256 compatibility_mask: 1,
4257 flags: IgvmPageDataFlags::new(),
4258 data_type: IgvmPageDataType::NORMAL,
4259 data: vec![0; size],
4260 };
4261
4262 match header.write_binary_header(&mut Vec::new(), &mut FileDataSerializer::new(0)) {
4263 Err(BinaryHeaderError::InvalidDataSize) => {}
4264 _ => {
4265 panic!("invalid serialization")
4266 }
4267 }
4268 }
4269
4270 #[test]
4271 fn test_parameter_area() {
4272 let file_data_offset = 1234;
4273
4274 let raw_header = IGVM_VHS_PARAMETER_AREA {
4276 number_of_bytes: PAGE_SIZE_4K,
4277 parameter_area_index: 2,
4278 file_offset: 0,
4279 };
4280
4281 let header = IgvmDirectiveHeader::ParameterArea {
4282 number_of_bytes: PAGE_SIZE_4K,
4283 parameter_area_index: 2,
4284 initial_data: Vec::new(),
4285 };
4286 test_variable_header(
4287 IgvmRevision::V1,
4288 header,
4289 0,
4290 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA,
4291 raw_header,
4292 None,
4293 None,
4294 );
4295
4296 let raw_header = IGVM_VHS_PARAMETER_AREA {
4297 number_of_bytes: PAGE_SIZE_4K,
4298 parameter_area_index: 2,
4299 file_offset: file_data_offset,
4300 };
4301 let mut file_data = vec![1, 2, 3, 4, 5, 0, 1];
4302 let header = IgvmDirectiveHeader::ParameterArea {
4303 number_of_bytes: PAGE_SIZE_4K,
4304 parameter_area_index: 2,
4305 initial_data: file_data.clone(),
4306 };
4307 file_data.resize(PAGE_SIZE_4K as usize, 0);
4308 test_variable_header(
4309 IgvmRevision::V1,
4310 header,
4311 file_data_offset,
4312 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA,
4313 raw_header,
4314 Some(file_data),
4315 None,
4316 );
4317
4318 let raw_header = IGVM_VHS_PARAMETER_AREA {
4320 number_of_bytes: 123 * PAGE_SIZE_4K,
4321 parameter_area_index: 2,
4322 file_offset: 0,
4323 };
4324
4325 let header = IgvmDirectiveHeader::ParameterArea {
4326 number_of_bytes: 123 * PAGE_SIZE_4K,
4327 parameter_area_index: 2,
4328 initial_data: Vec::new(),
4329 };
4330 test_variable_header(
4331 IgvmRevision::V1,
4332 header,
4333 0,
4334 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA,
4335 raw_header,
4336 None,
4337 None,
4338 );
4339
4340 let raw_header = IGVM_VHS_PARAMETER_AREA {
4341 number_of_bytes: 123 * PAGE_SIZE_4K,
4342 parameter_area_index: 2,
4343 file_offset: file_data_offset,
4344 };
4345 let mut file_data: Vec<u8> = (0..(PAGE_SIZE_4K + 1482))
4346 .map(|x| (x % 255) as u8)
4347 .collect();
4348 let header = IgvmDirectiveHeader::ParameterArea {
4349 number_of_bytes: 123 * PAGE_SIZE_4K,
4350 parameter_area_index: 2,
4351 initial_data: file_data.clone(),
4352 };
4353 file_data.resize(123 * PAGE_SIZE_4K as usize, 0);
4354 test_variable_header(
4355 IgvmRevision::V1,
4356 header,
4357 file_data_offset,
4358 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_AREA,
4359 raw_header,
4360 Some(file_data),
4361 None,
4362 );
4363 }
4364
4365 #[test]
4366 fn test_parameter_area_bad_size() {
4367 let header = IgvmDirectiveHeader::ParameterArea {
4369 number_of_bytes: 1234,
4370 parameter_area_index: 0,
4371 initial_data: Vec::new(),
4372 };
4373
4374 assert!(matches!(
4375 header.write_binary_header(&mut Vec::new(), &mut FileDataSerializer::new(0)),
4376 Err(BinaryHeaderError::UnalignedSize(1234))
4377 ));
4378 }
4379
4380 macro_rules! test_igvm_parameter {
4382 ($test_name:ident($directive:path, $header_type:path)) => {
4383 #[test]
4384 fn $test_name() {
4385 let raw_header = IGVM_VHS_PARAMETER {
4387 parameter_area_index: 1,
4388 byte_offset: 0,
4389 };
4390 let header = $directive(raw_header);
4391 test_variable_header(
4392 IgvmRevision::V1,
4393 header,
4394 0,
4395 $header_type,
4396 raw_header,
4397 None,
4398 None,
4399 );
4400
4401 let raw_header = IGVM_VHS_PARAMETER {
4403 parameter_area_index: 0,
4404 byte_offset: 1234,
4405 };
4406 let header = $directive(raw_header);
4407 test_variable_header(
4408 IgvmRevision::V1,
4409 header,
4410 0,
4411 $header_type,
4412 raw_header,
4413 None,
4414 None,
4415 );
4416 }
4417 };
4418 }
4419
4420 test_igvm_parameter!(test_vp_count(
4421 IgvmDirectiveHeader::VpCount,
4422 IgvmVariableHeaderType::IGVM_VHT_VP_COUNT_PARAMETER
4423 ));
4424
4425 test_igvm_parameter!(test_environment_info(
4426 IgvmDirectiveHeader::EnvironmentInfo,
4427 IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER
4428 ));
4429
4430 test_igvm_parameter!(test_srat(
4431 IgvmDirectiveHeader::Srat,
4432 IgvmVariableHeaderType::IGVM_VHT_SRAT
4433 ));
4434
4435 test_igvm_parameter!(test_madt(
4436 IgvmDirectiveHeader::Madt,
4437 IgvmVariableHeaderType::IGVM_VHT_MADT
4438 ));
4439
4440 test_igvm_parameter!(test_slit(
4441 IgvmDirectiveHeader::Slit,
4442 IgvmVariableHeaderType::IGVM_VHT_SLIT
4443 ));
4444
4445 test_igvm_parameter!(test_pptt(
4446 IgvmDirectiveHeader::Pptt,
4447 IgvmVariableHeaderType::IGVM_VHT_PPTT
4448 ));
4449
4450 test_igvm_parameter!(test_mmio_ranges(
4451 IgvmDirectiveHeader::MmioRanges,
4452 IgvmVariableHeaderType::IGVM_VHT_MMIO_RANGES
4453 ));
4454
4455 test_igvm_parameter!(test_memory_map(
4456 IgvmDirectiveHeader::MemoryMap,
4457 IgvmVariableHeaderType::IGVM_VHT_MEMORY_MAP
4458 ));
4459
4460 test_igvm_parameter!(test_command_line(
4461 IgvmDirectiveHeader::CommandLine,
4462 IgvmVariableHeaderType::IGVM_VHT_COMMAND_LINE
4463 ));
4464
4465 #[test]
4466 fn test_required_memory() {
4467 let gpa = 0x1234 * PAGE_SIZE_4K;
4468 let number_of_bytes = 0x4567 * PAGE_SIZE_4K as u32;
4469 let compatibility_mask = 0x1;
4470 let vtl2_protectable = true;
4471 let flags = RequiredMemoryFlags::new().with_vtl2_protectable(true);
4472 let raw_header = IGVM_VHS_REQUIRED_MEMORY {
4473 gpa,
4474 number_of_bytes,
4475 compatibility_mask,
4476 flags,
4477 ..FromZeros::new_zeroed()
4478 };
4479
4480 let header = IgvmDirectiveHeader::RequiredMemory {
4481 gpa,
4482 number_of_bytes,
4483 compatibility_mask,
4484 vtl2_protectable,
4485 };
4486 test_variable_header(
4487 IgvmRevision::V1,
4488 header,
4489 0,
4490 IgvmVariableHeaderType::IGVM_VHT_REQUIRED_MEMORY,
4491 raw_header,
4492 None,
4493 None,
4494 );
4495
4496 let gpa = 24 * 1024 * 1024;
4497 let number_of_bytes = 64 * 1024 * 1024;
4498 let compatibility_mask = 0x1;
4499 let flags = RequiredMemoryFlags::new();
4500 let raw_header = IGVM_VHS_REQUIRED_MEMORY {
4501 gpa,
4502 number_of_bytes,
4503 compatibility_mask,
4504 flags,
4505 ..FromZeros::new_zeroed()
4506 };
4507
4508 let header = IgvmDirectiveHeader::RequiredMemory {
4509 gpa,
4510 number_of_bytes,
4511 compatibility_mask,
4512 vtl2_protectable: false,
4513 };
4514 test_variable_header(
4515 IgvmRevision::V1,
4516 header,
4517 0,
4518 IgvmVariableHeaderType::IGVM_VHT_REQUIRED_MEMORY,
4519 raw_header,
4520 None,
4521 None,
4522 );
4523 }
4524
4525 #[test]
4526 fn test_required_memory_unaligned_gpa() {
4527 let gpa = 0x1234;
4528
4529 let header = IgvmDirectiveHeader::RequiredMemory {
4530 gpa,
4531 number_of_bytes: 0x4567 * PAGE_SIZE_4K as u32,
4532 compatibility_mask: 0x1,
4533 vtl2_protectable: true,
4534 };
4535 match header.write_binary_header(&mut Vec::new(), &mut FileDataSerializer::new(0)) {
4536 Err(BinaryHeaderError::UnalignedAddress(err_gpa)) => {
4537 assert_eq!(gpa, err_gpa);
4538 }
4539 _ => panic!("invalid serialization"),
4540 }
4541 }
4542
4543 #[test]
4544 fn test_required_memory_unaligned_size() {
4545 let size = 0x4567;
4546
4547 let header = IgvmDirectiveHeader::RequiredMemory {
4548 gpa: 0x1234 * PAGE_SIZE_4K,
4549 number_of_bytes: size,
4550 compatibility_mask: 0x1,
4551 vtl2_protectable: true,
4552 };
4553 match header.write_binary_header(&mut Vec::new(), &mut FileDataSerializer::new(0)) {
4554 Err(BinaryHeaderError::UnalignedSize(err_size)) => {
4555 assert_eq!(size as u64, err_size);
4556 }
4557 _ => panic!("invalid serialization"),
4558 }
4559 }
4560
4561 #[test]
4562 fn test_parameter_insert() {
4563 let raw_header = IGVM_VHS_PARAMETER_INSERT {
4564 gpa: 0x1234 * PAGE_SIZE_4K,
4565 compatibility_mask: 0x1,
4566 parameter_area_index: 0x10,
4567 };
4568
4569 let header = IgvmDirectiveHeader::ParameterInsert(raw_header);
4570 test_variable_header(
4571 IgvmRevision::V1,
4572 header,
4573 1234,
4574 IgvmVariableHeaderType::IGVM_VHT_PARAMETER_INSERT,
4575 raw_header,
4576 None,
4577 None,
4578 );
4579 }
4580
4581 #[test]
4582 fn test_parameter_insert_unaligned_gpa() {
4583 let gpa = 0x1234;
4584 let raw_header = IGVM_VHS_PARAMETER_INSERT {
4585 gpa,
4586 compatibility_mask: 0x1,
4587 parameter_area_index: 0x10,
4588 };
4589
4590 let header = IgvmDirectiveHeader::ParameterInsert(raw_header);
4591 match header.write_binary_header(&mut Vec::new(), &mut FileDataSerializer::new(0)) {
4592 Err(BinaryHeaderError::UnalignedAddress(err_gpa)) => {
4593 assert_eq!(gpa, err_gpa);
4594 }
4595 _ => panic!("invalid serialization"),
4596 }
4597 }
4598
4599 #[test]
4600 fn test_aarch64_vbs_vp_context() {
4601 let raw_header = IGVM_VHS_VP_CONTEXT {
4602 gpa: 0.into(),
4603 compatibility_mask: 0x1,
4604 file_offset: 1234,
4605 vp_index: 0,
4606 reserved: 0,
4607 };
4608
4609 let mut raw_header_bytes: [u8; 24] = [0; 24];
4610 raw_header_bytes[..raw_header.as_bytes().len()].copy_from_slice(raw_header.as_bytes());
4611
4612 let reg_list = [
4613 VbsVpContextRegister {
4614 vtl: 0,
4615 register_name: HvArm64RegisterName::XPc.0.into(),
4616 mbz: [0; 11],
4617 register_value: HvRegisterValue::from(0x1234u64).0.to_ne_bytes(),
4618 },
4619 VbsVpContextRegister {
4620 vtl: 0,
4621 register_name: HvArm64RegisterName::X1.0.into(),
4622 mbz: [0; 11],
4623 register_value: HvRegisterValue::from(0x5678u64).0.to_ne_bytes(),
4624 },
4625 ];
4626
4627 let reg_header = VbsVpContextHeader { register_count: 2 };
4628 let mut file_data: Vec<u8> = Vec::new();
4629 file_data.extend_from_slice(reg_header.as_bytes());
4630 file_data.extend_from_slice(reg_list.as_bytes());
4631
4632 let header = IgvmDirectiveHeader::AArch64VbsVpContext {
4633 vtl: Vtl::Vtl0,
4634 registers: vec![AArch64Register::Pc(0x1234), AArch64Register::X1(0x5678)],
4635 compatibility_mask: 0x1,
4636 };
4637
4638 test_variable_header(
4639 IgvmRevision::V2 {
4640 arch: Arch::AArch64,
4641 page_size: PAGE_SIZE_4K as u32,
4642 },
4643 header,
4644 1234,
4645 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
4646 raw_header_bytes,
4647 Some(file_data),
4648 Some(IgvmPlatformType::VSM_ISOLATION),
4649 )
4650 }
4651
4652 #[test]
4653 fn test_snp_vp_context() {
4654 let raw_header = IGVM_VHS_VP_CONTEXT {
4655 gpa: 0x1234000.into(),
4656 compatibility_mask: 0x1,
4657 file_offset: 1234,
4658 vp_index: 0xabcd,
4659 reserved: 0,
4660 };
4661
4662 let mut raw_header_bytes: [u8; 24] = [0; 24];
4663 raw_header_bytes[..raw_header.as_bytes().len()].copy_from_slice(raw_header.as_bytes());
4664
4665 let mut vmsa = SevVmsa::new_box_zeroed().unwrap();
4666 vmsa.cr2 = 42;
4668 vmsa.ldtr.attrib = 92;
4669
4670 let mut file_data: Vec<u8> = vmsa.as_bytes().to_vec();
4671 file_data.resize(PAGE_SIZE_4K.try_into().unwrap(), 0);
4672
4673 let header = IgvmDirectiveHeader::SnpVpContext {
4674 gpa: 0x1234000,
4675 compatibility_mask: 0x1,
4676 vp_index: 0xabcd,
4677 vmsa,
4678 };
4679
4680 test_variable_header(
4681 IgvmRevision::V2 {
4682 arch: Arch::X64,
4683 page_size: PAGE_SIZE_4K as u32,
4684 },
4685 header,
4686 1234,
4687 IgvmVariableHeaderType::IGVM_VHT_VP_CONTEXT,
4688 raw_header_bytes,
4689 Some(file_data),
4690 Some(IgvmPlatformType::SEV_SNP),
4691 )
4692 }
4693
4694 }