1use super::address_space::LocalMap;
7use super::address_space::init_local_map;
8use crate::AddressSpaceManager;
9use crate::ShimParams;
10use crate::arch::TdxHypercallPage;
11use crate::arch::x86_64::address_space::tdx_share_large_page;
12use crate::host_params::PartitionInfo;
13use crate::host_params::shim_params::IsolationType;
14use crate::hypercall::hvcall;
15use crate::memory::AllocationPolicy;
16use crate::memory::AllocationType;
17use crate::off_stack;
18use crate::single_threaded::SingleThreaded;
19use arrayvec::ArrayVec;
20use core::cell::RefCell;
21use loader_defs::shim::MemoryVtlType;
22use memory_range::MemoryRange;
23use page_table::x64::MappedRange;
24use page_table::x64::PAGE_TABLE_MAX_BYTES;
25use page_table::x64::PAGE_TABLE_MAX_COUNT;
26use page_table::x64::PageTable;
27use page_table::x64::PageTableBuilder;
28use sha2::Digest;
29use sha2::Sha384;
30use static_assertions::const_assert;
31use x86defs::X64_LARGE_PAGE_SIZE;
32use x86defs::tdx::TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT;
33use zerocopy::FromZeros;
34
35const DIAG_PAGE_SIZE: usize = hvdef::HV_PAGE_SIZE as usize;
67
68const DIAG_MAX_PAGES_PER_CHUNK: usize = X64_LARGE_PAGE_SIZE as usize / DIAG_PAGE_SIZE;
70const _: () = assert!(DIAG_MAX_PAGES_PER_CHUNK <= 512);
71
72const DIAG_BITMAP_WORDS: usize = DIAG_MAX_PAGES_PER_CHUNK / 64;
74
75const DIAG_MAX_BAD_PAGE_HASHES: usize = 32;
78
79const DIAG_MAX_CHUNKS: usize = 256;
82
83const DIAG_MAX_HASH_PAGES: usize = 32 * 1024;
87const DIAG_HASH_BITMAP_WORDS: usize = DIAG_MAX_HASH_PAGES / 64;
88
89const DIAG_MAX_SAVED_BAD_PAGES: usize = 3;
92
93#[derive(Copy, Clone)]
94struct DiagChunkHash {
95 gpa: u64,
96 len: u32,
97 phase_a_hash: [u8; 48],
98}
99
100#[derive(Copy, Clone)]
103struct DiagSavedBadPage {
104 page_idx: u32,
106 gpa: u64,
108 shim_hash: [u8; 48],
110 expected_hash: [u8; 48],
112 contents: [u8; DIAG_PAGE_SIZE],
114}
115
116const DIAG_EMPTY_SAVED_PAGE: DiagSavedBadPage = DiagSavedBadPage {
117 page_idx: 0,
118 gpa: 0,
119 shim_hash: [0; 48],
120 expected_hash: [0; 48],
121 contents: [0; DIAG_PAGE_SIZE],
122};
123
124struct DiagPerPageState {
128 expected: Option<&'static [loader_defs::paravisor::ExpectedPageHash]>,
132 seen: u32,
134 bad: u32,
136 overflow: bool,
140 bitmap: [u64; DIAG_HASH_BITMAP_WORDS],
145 saved_count: u32,
147 saved: [DiagSavedBadPage; DIAG_MAX_SAVED_BAD_PAGES],
150}
151
152impl DiagPerPageState {
153 const fn new_const() -> Self {
154 Self {
155 expected: None,
156 seen: 0,
157 bad: 0,
158 overflow: false,
159 bitmap: [0; DIAG_HASH_BITMAP_WORDS],
160 saved_count: 0,
161 saved: [DIAG_EMPTY_SAVED_PAGE; DIAG_MAX_SAVED_BAD_PAGES],
162 }
163 }
164}
165
166static DIAG_CHUNK_HASHES: SingleThreaded<RefCell<ArrayVec<DiagChunkHash, DIAG_MAX_CHUNKS>>> =
167 SingleThreaded(RefCell::new(ArrayVec::new_const()));
168
169static DIAG_RUNNING_A: SingleThreaded<RefCell<Option<Sha384>>> = SingleThreaded(RefCell::new(None));
170
171static DIAG_FULL_PAGE_DUMPED: SingleThreaded<core::cell::Cell<bool>> =
176 SingleThreaded(core::cell::Cell::new(false));
177
178static DIAG_PER_PAGE: SingleThreaded<RefCell<DiagPerPageState>> =
181 SingleThreaded(RefCell::new(DiagPerPageState::new_const()));
182
183fn diag_claim_full_page_dump() -> bool {
185 if DIAG_FULL_PAGE_DUMPED.0.get() {
186 return false;
187 }
188 DIAG_FULL_PAGE_DUMPED.0.set(true);
189 true
190}
191
192struct HexBytes<'a>(&'a [u8]);
195impl core::fmt::Display for HexBytes<'_> {
196 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
197 for b in self.0 {
198 write!(f, "{:02x}", b)?;
199 }
200 Ok(())
201 }
202}
203
204struct BitmapHex<'a> {
208 bitmap: &'a [u64],
209 total_pages: usize,
210}
211impl core::fmt::Display for BitmapHex<'_> {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213 let bits = self.total_pages.min(self.bitmap.len() * 64);
214 let bytes = bits.div_ceil(8);
215 for byte_idx in 0..bytes {
216 let word = byte_idx / 8;
217 let byte_in_word = byte_idx % 8;
218 let b = ((self.bitmap[word] >> (byte_in_word * 8)) & 0xff) as u8;
219 write!(f, "{:02x}", b)?;
220 }
221 Ok(())
222 }
223}
224
225struct RleRanges<'a> {
229 bitmap: &'a [u64],
230 total_pages: usize,
231}
232impl core::fmt::Display for RleRanges<'_> {
233 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234 let mut first = true;
235 let mut run_start: Option<usize> = None;
236 let mut prev_bit: bool = false;
237 for idx in 0..self.total_pages {
238 let word = idx / 64;
239 let bit = idx % 64;
240 let set = word < self.bitmap.len() && (self.bitmap[word] >> bit) & 1 == 1;
241 if set && !prev_bit {
242 run_start = Some(idx);
243 }
244 if !set && prev_bit {
245 let start = run_start.unwrap();
246 let end = idx - 1;
247 if !first {
248 write!(f, ",")?;
249 }
250 first = false;
251 if start == end {
252 write!(f, "{:#x}", start)?;
253 } else {
254 write!(f, "{:#x}-{:#x}", start, end)?;
255 }
256 run_start = None;
257 }
258 prev_bit = set;
259 }
260 if prev_bit {
262 let start = run_start.unwrap();
263 let end = self.total_pages - 1;
264 if !first {
265 write!(f, ",")?;
266 }
267 if start == end {
268 write!(f, "{:#x}", start)?;
269 } else {
270 write!(f, "{:#x}-{:#x}", start, end)?;
271 }
272 }
273 Ok(())
274 }
275}
276
277fn diag_record_phase_a(gpa: u64, data: &[u8]) {
287 {
288 let mut running = DIAG_RUNNING_A.0.borrow_mut();
289 if running.is_none() {
290 *running = Some(Sha384::new());
291 }
292 running.as_mut().unwrap().update(data);
293 }
294
295 let mut h = Sha384::new();
296 h.update(data);
297 let hash: [u8; 48] = h.finalize().into();
298
299 let mut chunks = DIAG_CHUNK_HASHES.0.borrow_mut();
300 if chunks
301 .try_push(DiagChunkHash {
302 gpa,
303 len: data.len() as u32,
304 phase_a_hash: hash,
305 })
306 .is_err()
307 {
308 log::error!(
309 "DIAG_CHUNK_OVERFLOW gpa={:#x} len={:#x} (increase DIAG_MAX_CHUNKS)",
310 gpa,
311 data.len(),
312 );
313 }
314 drop(chunks);
315
316 let mut state = DIAG_PER_PAGE.0.borrow_mut();
323 let expected_opt = state.expected;
324 if let Some(expected) = expected_opt {
325 for (page_off, page) in data.chunks(DIAG_PAGE_SIZE).enumerate() {
326 if page.len() < DIAG_PAGE_SIZE {
327 break;
330 }
331 let idx = state.seen as usize;
332 let page_gpa = gpa + (page_off * DIAG_PAGE_SIZE) as u64;
333
334 let mut ph = Sha384::new();
335 ph.update(page);
336 let shim_hash: [u8; 48] = ph.finalize().into();
337
338 if idx >= expected.len() {
339 state.overflow = true;
340 } else {
341 let expected_hash = expected[idx].sha384_hash;
342 if shim_hash != expected_hash {
343 state.bad = state.bad.saturating_add(1);
344 if idx < DIAG_MAX_HASH_PAGES {
345 let word = idx / 64;
346 let bit = idx % 64;
347 state.bitmap[word] |= 1u64 << bit;
348 }
349 let slot_idx = state.saved_count as usize;
350 if slot_idx < DIAG_MAX_SAVED_BAD_PAGES {
351 let slot = &mut state.saved[slot_idx];
352 slot.page_idx = idx as u32;
353 slot.gpa = page_gpa;
354 slot.shim_hash = shim_hash;
355 slot.expected_hash = expected_hash;
356 slot.contents.copy_from_slice(page);
357 state.saved_count += 1;
358 }
359 }
360 }
361 state.seen = state.seen.saturating_add(1);
362 }
363 }
364}
365
366fn diag_dump_full_page_diff(page_gpa: u64, phase: &str, pre: &[u8], post: &[u8]) {
370 debug_assert_eq!(pre.len(), DIAG_PAGE_SIZE);
371 debug_assert_eq!(post.len(), DIAG_PAGE_SIZE);
372 log::error!(
373 "DIAG_FIRST_BAD_PAGE_BEGIN page_gpa={:#x} phase={} len={:#x}",
374 page_gpa,
375 phase,
376 DIAG_PAGE_SIZE,
377 );
378 for (line_idx, (pre_line, post_line)) in pre.chunks(64).zip(post.chunks(64)).enumerate() {
379 let offset = line_idx * 64;
380 log::error!(
381 "DIAG_FIRST_BAD_PAGE_LINE page_gpa={:#x} offset={:#06x} pre={} post={}",
382 page_gpa,
383 offset,
384 HexBytes(pre_line),
385 HexBytes(post_line),
386 );
387 }
388 log::error!("DIAG_FIRST_BAD_PAGE_END page_gpa={:#x}", page_gpa);
389}
390
391fn diag_dump_full_page_single(page_gpa: u64, phase: &str, data: &[u8]) {
395 debug_assert!(data.len() >= DIAG_PAGE_SIZE);
396 log::error!(
397 "DIAG_FIRST_BAD_PAGE_BEGIN page_gpa={:#x} phase={} len={:#x}",
398 page_gpa,
399 phase,
400 DIAG_PAGE_SIZE,
401 );
402 for (line_idx, line) in data[..DIAG_PAGE_SIZE].chunks(64).enumerate() {
403 let offset = line_idx * 64;
404 log::error!(
405 "DIAG_FIRST_BAD_PAGE_LINE page_gpa={:#x} offset={:#06x} bytes={}",
406 page_gpa,
407 offset,
408 HexBytes(line),
409 );
410 }
411 log::error!("DIAG_FIRST_BAD_PAGE_END page_gpa={:#x}", page_gpa);
412}
413
414fn diag_dump_saved_page(saved: &DiagSavedBadPage) {
417 log::error!(
418 "DIAG_PAGE_HASH_BAD_BEGIN idx={} gpa={:#x} shim_hash={} expected_hash={} len={:#x}",
419 saved.page_idx,
420 saved.gpa,
421 HexBytes(&saved.shim_hash),
422 HexBytes(&saved.expected_hash),
423 DIAG_PAGE_SIZE,
424 );
425 for (line_idx, line) in saved.contents.chunks(64).enumerate() {
426 let offset = line_idx * 64;
427 log::error!(
428 "DIAG_PAGE_HASH_BAD_LINE idx={} gpa={:#x} offset={:#06x} bytes={}",
429 saved.page_idx,
430 saved.gpa,
431 offset,
432 HexBytes(line),
433 );
434 }
435 log::error!(
436 "DIAG_PAGE_HASH_BAD_END idx={} gpa={:#x}",
437 saved.page_idx,
438 saved.gpa,
439 );
440}
441
442fn diag_init_expected_hashes(shim_params: &ShimParams) {
449 let expected = shim_params.expected_page_hashes();
450 if expected.is_empty() {
451 log::info!(
452 "DIAG_EXPECTED_HASHES_META loader_count=0 \
453 (region absent or magic/version mismatch; per-page compare disabled)"
454 );
455 return;
456 }
457 DIAG_PER_PAGE.0.borrow_mut().expected = Some(expected);
458 log::info!("DIAG_EXPECTED_HASHES_META loader_count={}", expected.len(),);
459}
460
461fn diag_report_per_page_expected() {
466 let state = DIAG_PER_PAGE.0.borrow();
467 let loader_count = state.expected.map(|s| s.len()).unwrap_or(0);
468 log::error!(
469 "DIAG_PAGE_HASH_SUMMARY shim_seen={} bad={} loader_count={} overflow={}",
470 state.seen,
471 state.bad,
472 loader_count,
473 state.overflow,
474 );
475 if state.expected.is_none() {
476 return;
478 }
479 let bitmap_total = (state.seen as usize).min(DIAG_MAX_HASH_PAGES);
480 log::error!(
481 "DIAG_PAGE_HASH_BITMAP total_pages={} bad={} bitmap={}",
482 bitmap_total,
483 state.bad,
484 BitmapHex {
485 bitmap: &state.bitmap,
486 total_pages: bitmap_total,
487 },
488 );
489 log::error!(
490 "DIAG_PAGE_HASH_RANGES total_pages={} bad={} ranges={}",
491 bitmap_total,
492 state.bad,
493 RleRanges {
494 bitmap: &state.bitmap,
495 total_pages: bitmap_total,
496 },
497 );
498 for i in 0..(state.saved_count as usize) {
499 diag_dump_saved_page(&state.saved[i]);
500 }
501}
502
503fn diag_verify_phase_b(gpa: u64, pre: &[u8], post: &[u8]) {
518 if pre.len() != post.len() {
519 log::error!(
520 "DIAG_TRANSITION_LEN_MISMATCH gpa={:#x} pre_len={:#x} post_len={:#x}",
521 gpa,
522 pre.len(),
523 post.len(),
524 );
525 return;
526 }
527 if pre == post {
528 return;
529 }
530
531 let mut bitmap = [0u64; DIAG_BITMAP_WORDS];
534 let mut bad_count: usize = 0;
535 let mut first_bad_page_idx: Option<usize> = None;
536 let mut bad_hashes: ArrayVec<(u64, [u8; 48], [u8; 48]), DIAG_MAX_BAD_PAGE_HASHES> =
537 ArrayVec::new();
538 let total_pages = pre.len().div_ceil(DIAG_PAGE_SIZE);
539
540 for (idx, (pre_pg, post_pg)) in pre
541 .chunks(DIAG_PAGE_SIZE)
542 .zip(post.chunks(DIAG_PAGE_SIZE))
543 .enumerate()
544 {
545 if pre_pg == post_pg {
546 continue;
547 }
548 bad_count += 1;
549 if first_bad_page_idx.is_none() {
550 first_bad_page_idx = Some(idx);
551 }
552 let word = idx / 64;
553 let bit = idx % 64;
554 if word < bitmap.len() {
555 bitmap[word] |= 1u64 << bit;
556 }
557
558 if bad_hashes.len() < DIAG_MAX_BAD_PAGE_HASHES {
559 let mut ha = Sha384::new();
560 ha.update(pre_pg);
561 let hash_a: [u8; 48] = ha.finalize().into();
562 let mut hc = Sha384::new();
563 hc.update(post_pg);
564 let hash_c: [u8; 48] = hc.finalize().into();
565 let _ = bad_hashes.try_push((gpa + (idx * DIAG_PAGE_SIZE) as u64, hash_a, hash_c));
566 }
567 }
568
569 let mut ha = Sha384::new();
571 ha.update(pre);
572 let chunk_hash_a: [u8; 48] = ha.finalize().into();
573 let mut hb = Sha384::new();
574 hb.update(post);
575 let chunk_hash_b: [u8; 48] = hb.finalize().into();
576
577 log::error!(
578 "DIAG_TRANSITION_MISMATCH gpa={:#x} chunk_len={:#x} total_pages={} bad_pages={} \
579 chunk_phase_a={} chunk_phase_b={}",
580 gpa,
581 pre.len(),
582 total_pages,
583 bad_count,
584 HexBytes(&chunk_hash_a),
585 HexBytes(&chunk_hash_b),
586 );
587 log::error!(
588 "DIAG_TRANSITION_BITMAP gpa={:#x} total_pages={} bitmap={}",
589 gpa,
590 total_pages,
591 BitmapHex {
592 bitmap: &bitmap,
593 total_pages,
594 },
595 );
596 log::error!(
597 "DIAG_TRANSITION_PAGES gpa={:#x} count={} ranges={}",
598 gpa,
599 bad_count,
600 RleRanges {
601 bitmap: &bitmap,
602 total_pages,
603 },
604 );
605
606 for (page_gpa, ha, hb) in &bad_hashes {
607 log::error!(
608 "DIAG_BAD_PAGE_HASH phase=A_vs_B chunk_gpa={:#x} page_gpa={:#x} phase_a={} phase_b={}",
609 gpa,
610 page_gpa,
611 HexBytes(ha),
612 HexBytes(hb),
613 );
614 }
615 if bad_count > bad_hashes.len() {
616 log::error!(
617 "DIAG_BAD_PAGE_HASH_TRUNCATED chunk_gpa={:#x} shown={} total_bad={}",
618 gpa,
619 bad_hashes.len(),
620 bad_count,
621 );
622 }
623
624 if let Some(idx) = first_bad_page_idx {
625 if diag_claim_full_page_dump() {
626 let offset = idx * DIAG_PAGE_SIZE;
627 let pre_pg = &pre[offset..offset + DIAG_PAGE_SIZE];
628 let post_pg = &post[offset..offset + DIAG_PAGE_SIZE];
629 diag_dump_full_page_diff(gpa + offset as u64, "A_vs_B", pre_pg, post_pg);
630 }
631 }
632}
633
634fn diag_report_phase_c(expected_combined: &[u8]) {
645 let combined_a = DIAG_RUNNING_A.0.borrow_mut().take().map(|h| {
646 let out: [u8; 48] = h.finalize().into();
647 out
648 });
649
650 match combined_a {
651 Some(combined_a) => {
652 if combined_a.as_slice() == expected_combined {
653 log::error!(
654 "DIAG_VERDICT combined_phase_a matches expected: {} \
655 (host-supplied shared data was correct; corruption occurred at or after acceptance)",
656 HexBytes(&combined_a),
657 );
658 } else {
659 log::error!(
660 "DIAG_VERDICT combined_phase_a differs from expected: \
661 phase_a={} expected={} \
662 (host loaded incorrect data into shared pages)",
663 HexBytes(&combined_a),
664 HexBytes(expected_combined),
665 );
666 }
667 }
668 None => {
669 log::error!("DIAG_VERDICT combined_phase_a not captured (no chunks recorded)");
670 }
671 }
672
673 let chunks = DIAG_CHUNK_HASHES.0.borrow();
674 let total = chunks.len();
675 let mut mismatches: usize = 0;
676 for (idx, c) in chunks.iter().enumerate() {
677 let data = unsafe { core::slice::from_raw_parts(c.gpa as *const u8, c.len as usize) };
681 let mut h = Sha384::new();
682 h.update(data);
683 let hash_c: [u8; 48] = h.finalize().into();
684 if hash_c != c.phase_a_hash {
685 mismatches += 1;
686 log::error!(
687 "DIAG_POST_ACCEPT_MISMATCH idx={} gpa={:#x} len={:#x} phase_a={} phase_c={}",
688 idx,
689 c.gpa,
690 c.len,
691 HexBytes(&c.phase_a_hash),
692 HexBytes(&hash_c),
693 );
694 if data.len() >= DIAG_PAGE_SIZE && diag_claim_full_page_dump() {
704 diag_dump_full_page_single(c.gpa, "C", data);
705 }
706 }
707 }
708 if mismatches == 0 {
709 log::error!(
710 "DIAG_VERDICT all {} tracked chunks unchanged phase_a -> phase_c \
711 (combined mismatch is likely a layout/order/hashing disagreement)",
712 total,
713 );
714 } else {
715 log::error!(
716 "DIAG_VERDICT {} of {} tracked chunks changed between phase_a and phase_c",
717 mismatches,
718 total,
719 );
720 }
721
722 diag_report_per_page_expected();
727}
728
729pub fn setup_vtl2_memory(
732 shim_params: &ShimParams,
733 partition_info: &PartitionInfo,
734 address_space: &mut AddressSpaceManager,
735) {
736 if let IsolationType::None = shim_params.isolation_type {
743 return;
744 }
745
746 diag_init_expected_hashes(shim_params);
751
752 if let IsolationType::Vbs = shim_params.isolation_type {
753 let vsm_config = hvdef::HvRegisterVsmPartitionConfig::new()
756 .with_default_vtl_protection_mask(0xF)
757 .with_enable_vtl_protection(true);
758
759 hvcall()
760 .set_register(
761 hvdef::HvX64RegisterName::VsmPartitionConfig.into(),
762 hvdef::HvRegisterValue::from(u64::from(vsm_config)),
763 )
764 .expect("setting vsm config shouldn't fail");
765
766 let accepted_ranges =
771 shim_params
772 .imported_regions()
773 .filter_map(|(imported_range, already_accepted)| {
774 already_accepted.then_some(imported_range)
775 });
776 for range in memory_range::overlapping_ranges(
777 partition_info.vtl2_ram.iter().map(|entry| entry.range),
778 accepted_ranges,
779 ) {
780 hvcall()
781 .apply_vtl2_protections(range)
782 .expect("applying vtl 2 protections cannot fail");
783 }
784 }
785
786 let mut local_map = match shim_params.isolation_type {
789 IsolationType::Snp | IsolationType::Tdx => Some(init_local_map(
790 loader_defs::paravisor::PARAVISOR_LOCAL_MAP_VA,
791 )),
792 _ => None,
793 };
794
795 let mut last_range_end = None;
797 for (imported_range, _) in shim_params.imported_regions() {
798 assert!(last_range_end.is_none() || imported_range.start() > last_range_end.unwrap());
799 last_range_end = Some(imported_range.end() - hvdef::HV_PAGE_SIZE);
800 }
801
802 for range in memory_range::subtract_ranges(
805 partition_info.vtl2_ram.iter().map(|e| e.range),
806 shim_params.imported_regions().map(|(r, _)| r),
807 ) {
808 accept_vtl2_memory(shim_params, &mut local_map, range);
809 }
810
811 let ram_buffer = if let Some(bounce_buffer) = shim_params.bounce_buffer {
812 assert!(bounce_buffer.start() % X64_LARGE_PAGE_SIZE == 0);
813 assert!(bounce_buffer.len() >= X64_LARGE_PAGE_SIZE);
814
815 for range in memory_range::subtract_ranges(
816 core::iter::once(bounce_buffer),
817 partition_info.vtl2_ram.iter().map(|e| e.range),
818 ) {
819 accept_vtl2_memory(shim_params, &mut local_map, range);
820 }
821
822 unsafe {
826 core::slice::from_raw_parts_mut(
827 bounce_buffer.start() as *mut u8,
828 bounce_buffer.len() as usize,
829 )
830 }
831 } else {
832 &mut []
833 };
834
835 for (imported_range, already_accepted) in shim_params.imported_regions() {
838 if !already_accepted {
839 accept_pending_vtl2_memory(shim_params, &mut local_map, ram_buffer, imported_range);
840 }
841 }
842
843 if shim_params.isolation_type == IsolationType::Tdx {
846 let page_table_region = address_space
848 .allocate_aligned(
849 None,
850 PAGE_TABLE_MAX_BYTES as u64,
851 AllocationType::TdxPageTables,
852 AllocationPolicy::LowMemory,
853 X64_LARGE_PAGE_SIZE,
854 )
855 .expect("allocation of space for TDX page tables must succeed");
856
857 const_assert!((PAGE_TABLE_MAX_BYTES as u64) < X64_LARGE_PAGE_SIZE);
859 assert_eq!(page_table_region.range.start() % X64_LARGE_PAGE_SIZE, 0);
860
861 let mut local_map = local_map.expect("must be present on TDX");
862 let page_table_region_mapping = local_map.map_pages(page_table_region.range, false);
863 page_table_region_mapping.data.fill(0);
864
865 const MAX_RANGE_COUNT: usize = 64;
866 let mut ranges = off_stack!(
867 ArrayVec::<MappedRange, MAX_RANGE_COUNT>,
868 ArrayVec::new_const()
869 );
870
871 const AP_MEMORY_BOUNDARY: u64 = 4 * 1024 * 1024 * 1024;
874 let vtl2_ram = address_space
875 .vtl2_ranges()
876 .filter_map(|(range, typ)| match typ {
877 MemoryVtlType::VTL2_RAM => {
878 if range.start() < AP_MEMORY_BOUNDARY {
879 let end = if range.end() < AP_MEMORY_BOUNDARY {
880 range.end()
881 } else {
882 AP_MEMORY_BOUNDARY
883 };
884 Some(MappedRange::new(range.start(), end).read_only())
885 } else {
886 None
887 }
888 }
889 _ => None,
890 });
891
892 ranges.extend(vtl2_ram);
893
894 const PAGE_SIZE: u64 = 0x1000;
897 ranges.push(MappedRange::new(
898 x86defs::tdx::RESET_VECTOR_PAGE,
899 x86defs::tdx::RESET_VECTOR_PAGE + PAGE_SIZE,
900 ));
901
902 ranges.sort_by_key(|r| r.start());
903
904 let mut page_table_work_buffer =
905 off_stack!(ArrayVec<PageTable, PAGE_TABLE_MAX_COUNT>, ArrayVec::new_const());
906 for _ in 0..PAGE_TABLE_MAX_COUNT {
907 page_table_work_buffer.push(PageTable::new_zeroed());
908 }
909
910 PageTableBuilder::new(
911 page_table_region.range.start(),
912 page_table_work_buffer.as_mut_slice(),
913 page_table_region_mapping.data,
914 ranges.as_slice(),
915 )
916 .expect("page table builder must return no error")
917 .build()
918 .expect("page table construction must succeed");
919
920 crate::arch::tdx::tdx_prepare_ap_trampoline(page_table_region.range.start());
921
922 let free_buffer = ram_buffer.as_mut_ptr() as u64;
926 assert!(free_buffer.is_multiple_of(X64_LARGE_PAGE_SIZE));
927 let tdx_io_page = unsafe {
930 tdx_share_large_page(free_buffer);
931 TdxHypercallPage::new(free_buffer)
932 };
933 hvcall().initialize_tdx(tdx_io_page);
934 }
935}
936
937fn accept_vtl2_memory(
939 shim_params: &ShimParams,
940 local_map: &mut Option<LocalMap<'_>>,
941 range: MemoryRange,
942) {
943 match shim_params.isolation_type {
944 IsolationType::Vbs => {
945 hvcall()
946 .accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
947 .expect("accepting vtl 2 memory must not fail");
948 }
949 IsolationType::Snp => {
950 super::snp::set_page_acceptance(local_map.as_mut().unwrap(), range, true)
951 .expect("accepting vtl 2 memory must not fail");
952 }
953 IsolationType::Tdx => {
954 super::tdx::accept_pages(range).expect("accepting vtl2 memory must not fail")
955 }
956 _ => unreachable!(),
957 }
958}
959
960fn accept_pending_vtl2_memory(
963 shim_params: &ShimParams,
964 local_map: &mut Option<LocalMap<'_>>,
965 ram_buffer: &mut [u8],
966 range: MemoryRange,
967) {
968 let isolation_type = shim_params.isolation_type;
969
970 match isolation_type {
971 IsolationType::Vbs => {
972 hvcall()
973 .accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
974 .expect("accepting vtl 2 memory must not fail");
975 }
976 IsolationType::Snp | IsolationType::Tdx => {
977 let local_map = local_map.as_mut().unwrap();
978 let mut remaining = range;
984 while !remaining.is_empty() {
985 let range = MemoryRange::new(
987 remaining.start()
988 ..remaining.end().min(
989 (remaining.start() + X64_LARGE_PAGE_SIZE) & !(X64_LARGE_PAGE_SIZE - 1),
990 ),
991 );
992 remaining = MemoryRange::new(range.end()..remaining.end());
993
994 let ram_buffer = &mut ram_buffer[..range.len() as usize];
995
996 {
998 let map_range = if isolation_type == IsolationType::Tdx {
999 MemoryRange::new(
1001 range.start() | TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT
1002 ..range.end() | TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT,
1003 )
1004 } else {
1005 range
1006 };
1007
1008 let mapping = local_map.map_pages(map_range, false);
1009 ram_buffer.copy_from_slice(mapping.data);
1010
1011 if isolation_type == IsolationType::Snp {
1014 let mapping_va = mapping.data.as_ptr() as u64;
1015 for page_offset in
1016 (0..mapping.data.len() as u64).step_by(hvdef::HV_PAGE_SIZE as usize)
1017 {
1018 super::snp::cache_lines_flush_page(mapping_va + page_offset);
1019 }
1020 }
1021 }
1022
1023 diag_record_phase_a(range.start(), &ram_buffer[..]);
1028
1029 match isolation_type {
1031 IsolationType::Snp => {
1032 super::snp::Ghcb::change_page_visibility(range, false);
1033 }
1034 IsolationType::Tdx => {
1035 super::tdx::change_page_visibility(range, false);
1036 }
1037 _ => unreachable!(),
1038 }
1039
1040 match isolation_type {
1042 IsolationType::Snp => {
1043 super::snp::set_page_acceptance(local_map, range, true)
1044 .expect("accepting vtl 2 memory must not fail");
1045 }
1046 IsolationType::Tdx => {
1047 super::tdx::accept_pages(range)
1048 .expect("accepting vtl 2 memory must not fail");
1049 }
1050 _ => unreachable!(),
1051 }
1052
1053 {
1055 let mapping = unsafe {
1057 core::slice::from_raw_parts_mut(
1058 range.start() as *mut u8,
1059 range.len() as usize,
1060 )
1061 };
1062
1063 mapping.copy_from_slice(ram_buffer);
1064 }
1065
1066 {
1073 let post = unsafe {
1075 core::slice::from_raw_parts(
1076 range.start() as *const u8,
1077 range.len() as usize,
1078 )
1079 };
1080 diag_verify_phase_b(range.start(), &ram_buffer[..post.len()], post);
1081 }
1082 }
1083 }
1084 _ => unreachable!(),
1085 }
1086}
1087
1088pub fn verify_imported_regions_hash(shim_params: &ShimParams) {
1091 if let IsolationType::None = shim_params.isolation_type {
1094 return;
1095 }
1096
1097 if shim_params
1099 .imported_regions()
1100 .all(|(_, already_accepted)| already_accepted)
1101 {
1102 return;
1103 }
1104
1105 let mut hasher = Sha384::new();
1106 shim_params
1107 .imported_regions()
1108 .filter(|(_, already_accepted)| !already_accepted)
1109 .for_each(|(range, _)| {
1110 let mapping = unsafe {
1113 core::slice::from_raw_parts(range.start() as *const u8, range.len() as usize)
1114 };
1115 hasher.update(mapping);
1116 });
1117
1118 let final_hash: [u8; 48] = hasher.finalize().into();
1119 let expected = shim_params.imported_regions_hash();
1120 if final_hash.as_slice() != expected {
1121 log::error!(
1122 "DIAG_COMBINED_PHASE_C combined_phase_c={} expected={}",
1123 HexBytes(&final_hash),
1124 HexBytes(expected),
1125 );
1126 diag_report_phase_c(expected);
1127 panic!("Imported regions hash mismatch");
1128 }
1129}