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;
69
70const DIAG_MAX_PAGES_PER_CHUNK: usize = X64_LARGE_PAGE_SIZE as usize / DIAG_PAGE_SIZE;
72const _: () = assert!(DIAG_MAX_PAGES_PER_CHUNK <= 512);
73
74const DIAG_BITMAP_WORDS: usize = DIAG_MAX_PAGES_PER_CHUNK / 64;
76
77const DIAG_MAX_BAD_PAGE_HASHES: usize = 32;
80
81const DIAG_MAX_CHUNKS: usize = 256;
84
85const DIAG_MAX_HASH_PAGES: usize = 32 * 1024;
89const DIAG_HASH_BITMAP_WORDS: usize = DIAG_MAX_HASH_PAGES / 64;
90
91const DIAG_MAX_SAVED_BAD_PAGES: usize = 3;
94
95#[derive(Copy, Clone)]
96struct DiagChunkHash {
97 gpa: u64,
98 len: u32,
99 phase_a_hash: [u8; 48],
100}
101
102#[derive(Copy, Clone)]
105struct DiagSavedBadPage {
106 page_idx: u32,
108 gpa: u64,
110 shim_hash: [u8; 48],
112 expected_hash: [u8; 48],
114 contents: [u8; DIAG_PAGE_SIZE],
116}
117
118const DIAG_EMPTY_SAVED_PAGE: DiagSavedBadPage = DiagSavedBadPage {
119 page_idx: 0,
120 gpa: 0,
121 shim_hash: [0; 48],
122 expected_hash: [0; 48],
123 contents: [0; DIAG_PAGE_SIZE],
124};
125
126struct DiagPerPageState {
129 expected: Option<&'static [loader_defs::paravisor::ExpectedPageHash]>,
133 seen: u32,
135 bad: u32,
137 overflow: bool,
141 bitmap: [u64; DIAG_HASH_BITMAP_WORDS],
146 saved_count: u32,
148 saved: [DiagSavedBadPage; DIAG_MAX_SAVED_BAD_PAGES],
151}
152
153impl DiagPerPageState {
154 const fn new_const() -> Self {
155 Self {
156 expected: None,
157 seen: 0,
158 bad: 0,
159 overflow: false,
160 bitmap: [0; DIAG_HASH_BITMAP_WORDS],
161 saved_count: 0,
162 saved: [DIAG_EMPTY_SAVED_PAGE; DIAG_MAX_SAVED_BAD_PAGES],
163 }
164 }
165}
166
167static DIAG_CHUNK_HASHES: SingleThreaded<RefCell<ArrayVec<DiagChunkHash, DIAG_MAX_CHUNKS>>> =
168 SingleThreaded(RefCell::new(ArrayVec::new_const()));
169
170static DIAG_RUNNING_A: SingleThreaded<RefCell<Option<Sha384>>> = SingleThreaded(RefCell::new(None));
171
172static DIAG_FULL_PAGE_DUMPED: SingleThreaded<core::cell::Cell<bool>> =
177 SingleThreaded(core::cell::Cell::new(false));
178
179static DIAG_PER_PAGE: SingleThreaded<RefCell<DiagPerPageState>> =
182 SingleThreaded(RefCell::new(DiagPerPageState::new_const()));
183
184fn diag_claim_full_page_dump() -> bool {
186 if DIAG_FULL_PAGE_DUMPED.0.get() {
187 return false;
188 }
189 DIAG_FULL_PAGE_DUMPED.0.set(true);
190 true
191}
192
193struct HexBytes<'a>(&'a [u8]);
196impl core::fmt::Display for HexBytes<'_> {
197 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
198 for b in self.0 {
199 write!(f, "{:02x}", b)?;
200 }
201 Ok(())
202 }
203}
204
205struct BitmapHex<'a> {
209 bitmap: &'a [u64],
210 total_pages: usize,
211}
212impl core::fmt::Display for BitmapHex<'_> {
213 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 let bits = self.total_pages.min(self.bitmap.len() * 64);
215 let bytes = bits.div_ceil(8);
216 for byte_idx in 0..bytes {
217 let word = byte_idx / 8;
218 let byte_in_word = byte_idx % 8;
219 let b = ((self.bitmap[word] >> (byte_in_word * 8)) & 0xff) as u8;
220 write!(f, "{:02x}", b)?;
221 }
222 Ok(())
223 }
224}
225
226struct RleRanges<'a> {
230 bitmap: &'a [u64],
231 total_pages: usize,
232}
233impl core::fmt::Display for RleRanges<'_> {
234 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
235 let mut first = true;
236 let mut run_start: Option<usize> = None;
237 let mut prev_bit: bool = false;
238 for idx in 0..self.total_pages {
239 let word = idx / 64;
240 let bit = idx % 64;
241 let set = word < self.bitmap.len() && (self.bitmap[word] >> bit) & 1 == 1;
242 if set && !prev_bit {
243 run_start = Some(idx);
244 }
245 if !set && prev_bit {
246 let start = run_start.unwrap();
247 let end = idx - 1;
248 if !first {
249 write!(f, ",")?;
250 }
251 first = false;
252 if start == end {
253 write!(f, "{:#x}", start)?;
254 } else {
255 write!(f, "{:#x}-{:#x}", start, end)?;
256 }
257 run_start = None;
258 }
259 prev_bit = set;
260 }
261 if prev_bit {
263 let start = run_start.unwrap();
264 let end = self.total_pages - 1;
265 if !first {
266 write!(f, ",")?;
267 }
268 if start == end {
269 write!(f, "{:#x}", start)?;
270 } else {
271 write!(f, "{:#x}-{:#x}", start, end)?;
272 }
273 }
274 Ok(())
275 }
276}
277
278fn diag_record_phase_a(gpa: u64, data: &[u8]) {
284 {
285 let mut running = DIAG_RUNNING_A.0.borrow_mut();
286 if running.is_none() {
287 *running = Some(Sha384::new());
288 }
289 running.as_mut().unwrap().update(data);
290 }
291
292 let mut h = Sha384::new();
293 h.update(data);
294 let hash: [u8; 48] = h.finalize().into();
295
296 let mut chunks = DIAG_CHUNK_HASHES.0.borrow_mut();
297 if chunks
298 .try_push(DiagChunkHash {
299 gpa,
300 len: data.len() as u32,
301 phase_a_hash: hash,
302 })
303 .is_err()
304 {
305 log::error!(
306 "DIAG_CHUNK_OVERFLOW gpa={:#x} len={:#x} (increase DIAG_MAX_CHUNKS)",
307 gpa,
308 data.len(),
309 );
310 }
311}
312
313impl DiagPerPageState {
314 fn record(&mut self, gpa: u64, data: &[u8]) {
316 if let Some(expected) = self.expected {
317 for (page_off, page) in data.chunks_exact(DIAG_PAGE_SIZE).enumerate() {
318 let idx = self.seen as usize;
319 let page_gpa = gpa + (page_off * DIAG_PAGE_SIZE) as u64;
320
321 let shim_hash: [u8; 48] = Sha384::digest(page).into();
322
323 if idx >= expected.len() {
324 self.overflow = true;
325 } else {
326 let expected_hash = expected[idx].sha384_hash;
327 if shim_hash != expected_hash {
328 self.bad = self.bad.saturating_add(1);
329 log::error!(
330 "DIAG_PAGE_HASH_BAD_GPA idx={} gpa={:#x} bad_page_number={}",
331 idx,
332 page_gpa,
333 self.bad,
334 );
335 if idx < DIAG_MAX_HASH_PAGES {
336 let word = idx / 64;
337 let bit = idx % 64;
338 self.bitmap[word] |= 1u64 << bit;
339 }
340 let slot_idx = self.saved_count as usize;
341 if slot_idx < DIAG_MAX_SAVED_BAD_PAGES {
342 let slot = &mut self.saved[slot_idx];
343 slot.page_idx = idx as u32;
344 slot.gpa = page_gpa;
345 slot.shim_hash = shim_hash;
346 slot.expected_hash = expected_hash;
347 slot.contents.copy_from_slice(page);
348 self.saved_count += 1;
349 }
350 }
351 }
352 self.seen = self.seen.saturating_add(1);
353 }
354 }
355 }
356}
357
358fn diag_dump_full_page_diff(page_gpa: u64, phase: &str, pre: &[u8], post: &[u8]) {
362 debug_assert_eq!(pre.len(), DIAG_PAGE_SIZE);
363 debug_assert_eq!(post.len(), DIAG_PAGE_SIZE);
364 log::error!(
365 "DIAG_FIRST_BAD_PAGE_BEGIN page_gpa={:#x} phase={} len={:#x}",
366 page_gpa,
367 phase,
368 DIAG_PAGE_SIZE,
369 );
370 for (line_idx, (pre_line, post_line)) in pre.chunks(64).zip(post.chunks(64)).enumerate() {
371 let offset = line_idx * 64;
372 log::error!(
373 "DIAG_FIRST_BAD_PAGE_LINE page_gpa={:#x} offset={:#06x} pre={} post={}",
374 page_gpa,
375 offset,
376 HexBytes(pre_line),
377 HexBytes(post_line),
378 );
379 }
380 log::error!("DIAG_FIRST_BAD_PAGE_END page_gpa={:#x}", page_gpa);
381}
382
383fn diag_dump_full_page_single(page_gpa: u64, phase: &str, data: &[u8]) {
387 debug_assert!(data.len() >= DIAG_PAGE_SIZE);
388 log::error!(
389 "DIAG_FIRST_BAD_PAGE_BEGIN page_gpa={:#x} phase={} len={:#x}",
390 page_gpa,
391 phase,
392 DIAG_PAGE_SIZE,
393 );
394 for (line_idx, line) in data[..DIAG_PAGE_SIZE].chunks(64).enumerate() {
395 let offset = line_idx * 64;
396 log::error!(
397 "DIAG_FIRST_BAD_PAGE_LINE page_gpa={:#x} offset={:#06x} bytes={}",
398 page_gpa,
399 offset,
400 HexBytes(line),
401 );
402 }
403 log::error!("DIAG_FIRST_BAD_PAGE_END page_gpa={:#x}", page_gpa);
404}
405
406fn diag_dump_saved_page(saved: &DiagSavedBadPage, emit: &mut impl FnMut(core::fmt::Arguments<'_>)) {
409 emit(format_args!(
410 "DIAG_PAGE_HASH_BAD_BEGIN idx={} gpa={:#x} shim_hash={} expected_hash={} len={:#x}",
411 saved.page_idx,
412 saved.gpa,
413 HexBytes(&saved.shim_hash),
414 HexBytes(&saved.expected_hash),
415 DIAG_PAGE_SIZE,
416 ));
417 for (line_idx, line) in saved.contents.chunks(64).enumerate() {
418 let offset = line_idx * 64;
419 emit(format_args!(
420 "DIAG_PAGE_HASH_BAD_LINE idx={} gpa={:#x} offset={:#06x} bytes={}",
421 saved.page_idx,
422 saved.gpa,
423 offset,
424 HexBytes(line),
425 ));
426 }
427 emit(format_args!(
428 "DIAG_PAGE_HASH_BAD_END idx={} gpa={:#x}",
429 saved.page_idx, saved.gpa,
430 ));
431}
432
433fn diag_init_expected_hashes(shim_params: &ShimParams) {
440 let expected = shim_params.expected_page_hashes();
441 if expected.is_empty() {
442 log::info!(
443 "DIAG_EXPECTED_HASHES_META loader_count=0 \
444 (region absent or magic/version mismatch; per-page compare disabled)"
445 );
446 return;
447 }
448 DIAG_PER_PAGE.0.borrow_mut().expected = Some(expected);
449 log::info!("DIAG_EXPECTED_HASHES_META loader_count={}", expected.len(),);
450}
451
452fn diag_report_per_page_expected() {
458 let state = DIAG_PER_PAGE.0.borrow();
459 diag_emit_per_page_expected(&state, |message| log::error!("{}", message));
460}
461
462fn diag_emit_per_page_expected(
463 state: &DiagPerPageState,
464 mut emit: impl FnMut(core::fmt::Arguments<'_>),
465) {
466 let loader_count = state.expected.map(|s| s.len()).unwrap_or(0);
467 emit(format_args!(
468 "DIAG_PAGE_HASH_SUMMARY shim_seen={} bad={} loader_count={} overflow={}",
469 state.seen, state.bad, loader_count, state.overflow,
470 ));
471 if state.expected.is_none() {
472 return;
474 }
475 let bitmap_total = (state.seen as usize).min(DIAG_MAX_HASH_PAGES);
476 emit(format_args!(
477 "DIAG_PAGE_HASH_BITMAP total_pages={} bad={} bitmap={}",
478 bitmap_total,
479 state.bad,
480 BitmapHex {
481 bitmap: &state.bitmap,
482 total_pages: bitmap_total,
483 },
484 ));
485 emit(format_args!(
486 "DIAG_PAGE_HASH_RANGES total_pages={} bad={} ranges={}",
487 bitmap_total,
488 state.bad,
489 RleRanges {
490 bitmap: &state.bitmap,
491 total_pages: bitmap_total,
492 },
493 ));
494 for i in 0..(state.saved_count as usize) {
495 diag_dump_saved_page(&state.saved[i], &mut emit);
496 }
497}
498
499fn diag_verify_shared_imported_regions(shim_params: &ShimParams, local_map: &mut LocalMap<'_>) {
503 let mut hasher = Sha384::new();
504 let mut total_pages = 0;
505 for (range, already_accepted) in shim_params.imported_regions() {
506 if already_accepted {
507 continue;
508 }
509 for chunk in pending_import_chunks(range) {
510 let mapping = local_map.map_pages(chunk, false);
511 hasher.update(&*mapping.data);
512 DIAG_PER_PAGE
513 .0
514 .borrow_mut()
515 .record(chunk.start(), mapping.data);
516 total_pages += chunk.len() / hvdef::HV_PAGE_SIZE;
517 }
518 }
519 if total_pages == 0 {
520 return;
521 }
522
523 let hash: [u8; 48] = hasher.finalize().into();
524 let expected = shim_params.imported_regions_hash();
525 if hash.as_slice() != expected || DIAG_PER_PAGE.0.borrow().bad != 0 {
526 log::error!(
527 "DIAG_SHARED_PREFLIGHT_MISMATCH total_pages={} combined_shared={} expected={}",
528 total_pages,
529 HexBytes(&hash),
530 HexBytes(expected),
531 );
532 diag_report_per_page_expected();
533 panic!("Imported regions hash mismatch");
534 }
535}
536
537fn pending_import_chunks(mut remaining: MemoryRange) -> impl Iterator<Item = MemoryRange> {
539 core::iter::from_fn(move || {
540 if remaining.is_empty() {
541 return None;
542 }
543 let end = remaining
544 .end()
545 .min((remaining.start() + X64_LARGE_PAGE_SIZE) & !(X64_LARGE_PAGE_SIZE - 1));
546 let chunk = MemoryRange::new(remaining.start()..end);
547 remaining = MemoryRange::new(end..remaining.end());
548 Some(chunk)
549 })
550}
551
552fn diag_verify_phase_b(gpa: u64, pre: &[u8], post: &[u8]) {
567 if pre.len() != post.len() {
568 log::error!(
569 "DIAG_TRANSITION_LEN_MISMATCH gpa={:#x} pre_len={:#x} post_len={:#x}",
570 gpa,
571 pre.len(),
572 post.len(),
573 );
574 return;
575 }
576 if pre == post {
577 return;
578 }
579
580 let mut bitmap = [0u64; DIAG_BITMAP_WORDS];
583 let mut bad_count: usize = 0;
584 let mut first_bad_page_idx: Option<usize> = None;
585 let mut bad_hashes: ArrayVec<(u64, [u8; 48], [u8; 48]), DIAG_MAX_BAD_PAGE_HASHES> =
586 ArrayVec::new();
587 let total_pages = pre.len().div_ceil(DIAG_PAGE_SIZE);
588
589 for (idx, (pre_pg, post_pg)) in pre
590 .chunks(DIAG_PAGE_SIZE)
591 .zip(post.chunks(DIAG_PAGE_SIZE))
592 .enumerate()
593 {
594 if pre_pg == post_pg {
595 continue;
596 }
597 bad_count += 1;
598 if first_bad_page_idx.is_none() {
599 first_bad_page_idx = Some(idx);
600 }
601 let word = idx / 64;
602 let bit = idx % 64;
603 if word < bitmap.len() {
604 bitmap[word] |= 1u64 << bit;
605 }
606
607 if bad_hashes.len() < DIAG_MAX_BAD_PAGE_HASHES {
608 let mut ha = Sha384::new();
609 ha.update(pre_pg);
610 let hash_a: [u8; 48] = ha.finalize().into();
611 let mut hc = Sha384::new();
612 hc.update(post_pg);
613 let hash_c: [u8; 48] = hc.finalize().into();
614 let _ = bad_hashes.try_push((gpa + (idx * DIAG_PAGE_SIZE) as u64, hash_a, hash_c));
615 }
616 }
617
618 let mut ha = Sha384::new();
620 ha.update(pre);
621 let chunk_hash_a: [u8; 48] = ha.finalize().into();
622 let mut hb = Sha384::new();
623 hb.update(post);
624 let chunk_hash_b: [u8; 48] = hb.finalize().into();
625
626 log::error!(
627 "DIAG_TRANSITION_MISMATCH gpa={:#x} chunk_len={:#x} total_pages={} bad_pages={} \
628 chunk_phase_a={} chunk_phase_b={}",
629 gpa,
630 pre.len(),
631 total_pages,
632 bad_count,
633 HexBytes(&chunk_hash_a),
634 HexBytes(&chunk_hash_b),
635 );
636 log::error!(
637 "DIAG_TRANSITION_BITMAP gpa={:#x} total_pages={} bitmap={}",
638 gpa,
639 total_pages,
640 BitmapHex {
641 bitmap: &bitmap,
642 total_pages,
643 },
644 );
645 log::error!(
646 "DIAG_TRANSITION_PAGES gpa={:#x} count={} ranges={}",
647 gpa,
648 bad_count,
649 RleRanges {
650 bitmap: &bitmap,
651 total_pages,
652 },
653 );
654
655 for (page_gpa, ha, hb) in &bad_hashes {
656 log::error!(
657 "DIAG_BAD_PAGE_HASH phase=A_vs_B chunk_gpa={:#x} page_gpa={:#x} phase_a={} phase_b={}",
658 gpa,
659 page_gpa,
660 HexBytes(ha),
661 HexBytes(hb),
662 );
663 }
664 if bad_count > bad_hashes.len() {
665 log::error!(
666 "DIAG_BAD_PAGE_HASH_TRUNCATED chunk_gpa={:#x} shown={} total_bad={}",
667 gpa,
668 bad_hashes.len(),
669 bad_count,
670 );
671 }
672
673 if let Some(idx) = first_bad_page_idx {
674 if diag_claim_full_page_dump() {
675 let offset = idx * DIAG_PAGE_SIZE;
676 let pre_pg = &pre[offset..offset + DIAG_PAGE_SIZE];
677 let post_pg = &post[offset..offset + DIAG_PAGE_SIZE];
678 diag_dump_full_page_diff(gpa + offset as u64, "A_vs_B", pre_pg, post_pg);
679 }
680 }
681}
682
683fn diag_report_phase_c(expected_combined: &[u8]) {
694 let combined_a = DIAG_RUNNING_A.0.borrow_mut().take().map(|h| {
695 let out: [u8; 48] = h.finalize().into();
696 out
697 });
698
699 match combined_a {
700 Some(combined_a) => {
701 if combined_a.as_slice() == expected_combined {
702 log::error!(
703 "DIAG_VERDICT combined_phase_a matches expected: {} \
704 (host-supplied shared data was correct; corruption occurred at or after acceptance)",
705 HexBytes(&combined_a),
706 );
707 } else {
708 log::error!(
709 "DIAG_VERDICT combined_phase_a differs from expected: \
710 phase_a={} expected={} \
711 (host loaded incorrect data into shared pages)",
712 HexBytes(&combined_a),
713 HexBytes(expected_combined),
714 );
715 }
716 }
717 None => {
718 log::error!("DIAG_VERDICT combined_phase_a not captured (no chunks recorded)");
719 }
720 }
721
722 let chunks = DIAG_CHUNK_HASHES.0.borrow();
723 let total = chunks.len();
724 let mut mismatches: usize = 0;
725 for (idx, c) in chunks.iter().enumerate() {
726 let data = unsafe { core::slice::from_raw_parts(c.gpa as *const u8, c.len as usize) };
730 let mut h = Sha384::new();
731 h.update(data);
732 let hash_c: [u8; 48] = h.finalize().into();
733 if hash_c != c.phase_a_hash {
734 mismatches += 1;
735 log::error!(
736 "DIAG_POST_ACCEPT_MISMATCH idx={} gpa={:#x} len={:#x} phase_a={} phase_c={}",
737 idx,
738 c.gpa,
739 c.len,
740 HexBytes(&c.phase_a_hash),
741 HexBytes(&hash_c),
742 );
743 if data.len() >= DIAG_PAGE_SIZE && diag_claim_full_page_dump() {
753 diag_dump_full_page_single(c.gpa, "C", data);
754 }
755 }
756 }
757 if mismatches == 0 {
758 log::error!(
759 "DIAG_VERDICT all {} tracked chunks unchanged phase_a -> phase_c \
760 (combined mismatch is likely a layout/order/hashing disagreement)",
761 total,
762 );
763 } else {
764 log::error!(
765 "DIAG_VERDICT {} of {} tracked chunks changed between phase_a and phase_c",
766 mismatches,
767 total,
768 );
769 }
770
771 diag_report_per_page_expected();
776}
777
778pub fn setup_vtl2_memory(
781 shim_params: &ShimParams,
782 partition_info: &PartitionInfo,
783 address_space: &mut AddressSpaceManager,
784) {
785 if let IsolationType::None = shim_params.isolation_type {
792 return;
793 }
794
795 diag_init_expected_hashes(shim_params);
797
798 if let IsolationType::Vbs = shim_params.isolation_type {
799 let vsm_config = hvdef::HvRegisterVsmPartitionConfig::new()
802 .with_default_vtl_protection_mask(0xF)
803 .with_enable_vtl_protection(true);
804
805 hvcall()
806 .set_register(
807 hvdef::HvX64RegisterName::VsmPartitionConfig.into(),
808 hvdef::HvRegisterValue::from(u64::from(vsm_config)),
809 )
810 .expect("setting vsm config shouldn't fail");
811
812 let accepted_ranges =
817 shim_params
818 .imported_regions()
819 .filter_map(|(imported_range, already_accepted)| {
820 already_accepted.then_some(imported_range)
821 });
822 for range in memory_range::overlapping_ranges(
823 partition_info.vtl2_ram.iter().map(|entry| entry.range),
824 accepted_ranges,
825 ) {
826 hvcall()
827 .apply_vtl2_protections(range)
828 .expect("applying vtl 2 protections cannot fail");
829 }
830 }
831
832 let mut local_map = match shim_params.isolation_type {
835 IsolationType::Snp | IsolationType::Tdx => Some(init_local_map(
836 loader_defs::paravisor::PARAVISOR_LOCAL_MAP_VA,
837 )),
838 _ => None,
839 };
840
841 let mut last_range_end = None;
843 for (imported_range, _) in shim_params.imported_regions() {
844 assert!(last_range_end.is_none() || imported_range.start() > last_range_end.unwrap());
845 last_range_end = Some(imported_range.end() - hvdef::HV_PAGE_SIZE);
846 }
847
848 if shim_params.isolation_type == IsolationType::Snp {
849 diag_verify_shared_imported_regions(shim_params, local_map.as_mut().unwrap());
850 }
851
852 for range in memory_range::subtract_ranges(
855 partition_info.vtl2_ram.iter().map(|e| e.range),
856 shim_params.imported_regions().map(|(r, _)| r),
857 ) {
858 accept_vtl2_memory(shim_params, &mut local_map, range);
859 }
860
861 let ram_buffer = if let Some(bounce_buffer) = shim_params.bounce_buffer {
862 assert!(bounce_buffer.start() % X64_LARGE_PAGE_SIZE == 0);
863 assert!(bounce_buffer.len() >= X64_LARGE_PAGE_SIZE);
864
865 for range in memory_range::subtract_ranges(
866 core::iter::once(bounce_buffer),
867 partition_info.vtl2_ram.iter().map(|e| e.range),
868 ) {
869 accept_vtl2_memory(shim_params, &mut local_map, range);
870 }
871
872 unsafe {
876 core::slice::from_raw_parts_mut(
877 bounce_buffer.start() as *mut u8,
878 bounce_buffer.len() as usize,
879 )
880 }
881 } else {
882 &mut []
883 };
884
885 for (imported_range, already_accepted) in shim_params.imported_regions() {
888 if !already_accepted {
889 accept_pending_vtl2_memory(shim_params, &mut local_map, ram_buffer, imported_range);
890 }
891 }
892
893 if shim_params.isolation_type == IsolationType::Tdx {
896 let page_table_region = address_space
898 .allocate_aligned(
899 None,
900 PAGE_TABLE_MAX_BYTES as u64,
901 AllocationType::TdxPageTables,
902 AllocationPolicy::LowMemory,
903 X64_LARGE_PAGE_SIZE,
904 )
905 .expect("allocation of space for TDX page tables must succeed");
906
907 const_assert!((PAGE_TABLE_MAX_BYTES as u64) < X64_LARGE_PAGE_SIZE);
909 assert_eq!(page_table_region.range.start() % X64_LARGE_PAGE_SIZE, 0);
910
911 let mut local_map = local_map.expect("must be present on TDX");
912 let page_table_region_mapping = local_map.map_pages(page_table_region.range, false);
913 page_table_region_mapping.data.fill(0);
914
915 const MAX_RANGE_COUNT: usize = 64;
916 let mut ranges = off_stack!(
917 ArrayVec::<MappedRange, MAX_RANGE_COUNT>,
918 ArrayVec::new_const()
919 );
920
921 const AP_MEMORY_BOUNDARY: u64 = 4 * 1024 * 1024 * 1024;
924 let vtl2_ram = address_space
925 .vtl2_ranges()
926 .filter_map(|(range, typ)| match typ {
927 MemoryVtlType::VTL2_RAM => {
928 if range.start() < AP_MEMORY_BOUNDARY {
929 let end = if range.end() < AP_MEMORY_BOUNDARY {
930 range.end()
931 } else {
932 AP_MEMORY_BOUNDARY
933 };
934 Some(MappedRange::new(range.start(), end).read_only())
935 } else {
936 None
937 }
938 }
939 _ => None,
940 });
941
942 ranges.extend(vtl2_ram);
943
944 const PAGE_SIZE: u64 = 0x1000;
947 ranges.push(MappedRange::new(
948 x86defs::tdx::RESET_VECTOR_PAGE,
949 x86defs::tdx::RESET_VECTOR_PAGE + PAGE_SIZE,
950 ));
951
952 ranges.sort_by_key(|r| r.start());
953
954 let mut page_table_work_buffer =
955 off_stack!(ArrayVec<PageTable, PAGE_TABLE_MAX_COUNT>, ArrayVec::new_const());
956 for _ in 0..PAGE_TABLE_MAX_COUNT {
957 page_table_work_buffer.push(PageTable::new_zeroed());
958 }
959
960 PageTableBuilder::new(
961 page_table_region.range.start(),
962 page_table_work_buffer.as_mut_slice(),
963 page_table_region_mapping.data,
964 ranges.as_slice(),
965 )
966 .expect("page table builder must return no error")
967 .build()
968 .expect("page table construction must succeed");
969
970 crate::arch::tdx::tdx_prepare_ap_trampoline(page_table_region.range.start());
971
972 let free_buffer = ram_buffer.as_mut_ptr() as u64;
976 assert!(free_buffer.is_multiple_of(X64_LARGE_PAGE_SIZE));
977 let tdx_io_page = unsafe {
980 tdx_share_large_page(free_buffer);
981 TdxHypercallPage::new(free_buffer)
982 };
983 hvcall().initialize_tdx(tdx_io_page);
984 }
985}
986
987fn accept_vtl2_memory(
989 shim_params: &ShimParams,
990 local_map: &mut Option<LocalMap<'_>>,
991 range: MemoryRange,
992) {
993 match shim_params.isolation_type {
994 IsolationType::Vbs => {
995 hvcall()
996 .accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
997 .expect("accepting vtl 2 memory must not fail");
998 }
999 IsolationType::Snp => {
1000 super::snp::set_page_acceptance(local_map.as_mut().unwrap(), range, true)
1001 .expect("accepting vtl 2 memory must not fail");
1002 }
1003 IsolationType::Tdx => {
1004 super::tdx::accept_pages(range).expect("accepting vtl2 memory must not fail")
1005 }
1006 _ => unreachable!(),
1007 }
1008}
1009
1010fn accept_pending_vtl2_memory(
1013 shim_params: &ShimParams,
1014 local_map: &mut Option<LocalMap<'_>>,
1015 ram_buffer: &mut [u8],
1016 range: MemoryRange,
1017) {
1018 let isolation_type = shim_params.isolation_type;
1019
1020 match isolation_type {
1021 IsolationType::Vbs => {
1022 hvcall()
1023 .accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
1024 .expect("accepting vtl 2 memory must not fail");
1025 }
1026 IsolationType::Snp | IsolationType::Tdx => {
1027 let local_map = local_map.as_mut().unwrap();
1028 for range in pending_import_chunks(range) {
1034 let ram_buffer = &mut ram_buffer[..range.len() as usize];
1035
1036 {
1038 let map_range = if isolation_type == IsolationType::Tdx {
1039 MemoryRange::new(
1041 range.start() | TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT
1042 ..range.end() | TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT,
1043 )
1044 } else {
1045 range
1046 };
1047
1048 let mapping = local_map.map_pages(map_range, false);
1049 ram_buffer.copy_from_slice(mapping.data);
1050
1051 if isolation_type == IsolationType::Snp {
1054 let mapping_va = mapping.data.as_ptr() as u64;
1055 for page_offset in
1056 (0..mapping.data.len() as u64).step_by(hvdef::HV_PAGE_SIZE as usize)
1057 {
1058 super::snp::cache_lines_flush_page(mapping_va + page_offset);
1059 }
1060 }
1061 }
1062
1063 diag_record_phase_a(range.start(), &ram_buffer[..]);
1068 if isolation_type == IsolationType::Tdx {
1069 DIAG_PER_PAGE
1070 .0
1071 .borrow_mut()
1072 .record(range.start(), ram_buffer);
1073 }
1074
1075 match isolation_type {
1077 IsolationType::Snp => {
1078 super::snp::Ghcb::change_page_visibility(range, false);
1079 }
1080 IsolationType::Tdx => {
1081 super::tdx::change_page_visibility(range, false);
1082 }
1083 _ => unreachable!(),
1084 }
1085
1086 match isolation_type {
1088 IsolationType::Snp => {
1089 super::snp::set_page_acceptance(local_map, range, true)
1090 .expect("accepting vtl 2 memory must not fail");
1091 }
1092 IsolationType::Tdx => {
1093 super::tdx::accept_pages(range)
1094 .expect("accepting vtl 2 memory must not fail");
1095 }
1096 _ => unreachable!(),
1097 }
1098
1099 {
1101 let mapping = unsafe {
1103 core::slice::from_raw_parts_mut(
1104 range.start() as *mut u8,
1105 range.len() as usize,
1106 )
1107 };
1108
1109 mapping.copy_from_slice(ram_buffer);
1110 }
1111
1112 {
1119 let post = unsafe {
1121 core::slice::from_raw_parts(
1122 range.start() as *const u8,
1123 range.len() as usize,
1124 )
1125 };
1126 diag_verify_phase_b(range.start(), &ram_buffer[..post.len()], post);
1127 }
1128 }
1129 }
1130 _ => unreachable!(),
1131 }
1132}
1133
1134pub fn verify_imported_regions_hash(shim_params: &ShimParams) {
1137 if let IsolationType::None = shim_params.isolation_type {
1140 return;
1141 }
1142
1143 if shim_params
1145 .imported_regions()
1146 .all(|(_, already_accepted)| already_accepted)
1147 {
1148 return;
1149 }
1150
1151 let mut hasher = Sha384::new();
1152 shim_params
1153 .imported_regions()
1154 .filter(|(_, already_accepted)| !already_accepted)
1155 .for_each(|(range, _)| {
1156 let mapping = unsafe {
1159 core::slice::from_raw_parts(range.start() as *const u8, range.len() as usize)
1160 };
1161 hasher.update(mapping);
1162 });
1163
1164 let final_hash: [u8; 48] = hasher.finalize().into();
1165 let expected = shim_params.imported_regions_hash();
1166 if final_hash.as_slice() != expected {
1167 log::error!(
1168 "DIAG_COMBINED_PHASE_C combined_phase_c={} expected={}",
1169 HexBytes(&final_hash),
1170 HexBytes(expected),
1171 );
1172 diag_report_phase_c(expected);
1173 panic!("Imported regions hash mismatch");
1174 }
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179 use super::*;
1180 use loader_defs::paravisor::ExpectedPageHash;
1181 use test_with_tracing::test;
1182
1183 fn expected_zero_pages(count: usize) -> DiagPerPageState {
1184 let expected = vec![
1185 ExpectedPageHash {
1186 sha384_hash: Sha384::digest([0; DIAG_PAGE_SIZE]).into(),
1187 };
1188 count
1189 ];
1190 DiagPerPageState {
1191 expected: Some(Box::leak(expected.into_boxed_slice())),
1192 ..DiagPerPageState::new_const()
1193 }
1194 }
1195
1196 #[test]
1197 fn shared_scan_tracks_noncontiguous_gpas_and_host_message_order() {
1198 let mut state = expected_zero_pages(5);
1199 let good = [0; DIAG_PAGE_SIZE];
1200 let bad = [0xab; DIAG_PAGE_SIZE];
1201 state.record(0x1ff000, &good);
1202 state.record(0x200000, &bad);
1203 state.record(0x800000, &good);
1204 state.record(0x801000, &bad);
1205 state.record(0x802000, &bad);
1206
1207 assert_eq!(state.seen, 5);
1208 assert_eq!(state.bad, 3);
1209 assert!(!state.overflow);
1210 assert_eq!(state.bitmap[0], 0b11010);
1211 assert_eq!(state.saved_count, 3);
1212 for (saved, (idx, gpa)) in
1213 state
1214 .saved
1215 .iter()
1216 .zip([(1, 0x200000), (3, 0x801000), (4, 0x802000)])
1217 {
1218 assert_eq!(saved.page_idx, idx);
1219 assert_eq!(saved.gpa, gpa);
1220 assert_eq!(saved.contents, bad);
1221 assert_eq!(saved.shim_hash, <[u8; 48]>::from(Sha384::digest(bad)));
1222 assert_eq!(saved.expected_hash, state.expected.unwrap()[0].sha384_hash);
1223 }
1224
1225 let mut messages = Vec::new();
1226 diag_emit_per_page_expected(&state, |message| {
1227 messages.push(format!("[ERROR] {message}"));
1228 });
1229 assert_eq!(
1230 messages[0],
1231 "[ERROR] DIAG_PAGE_HASH_SUMMARY shim_seen=5 bad=3 loader_count=5 overflow=false"
1232 );
1233 assert_eq!(
1234 messages[1],
1235 "[ERROR] DIAG_PAGE_HASH_BITMAP total_pages=5 bad=3 bitmap=1a"
1236 );
1237 assert_eq!(
1238 messages[2],
1239 "[ERROR] DIAG_PAGE_HASH_RANGES total_pages=5 bad=3 ranges=0x1,0x3-0x4"
1240 );
1241 assert_eq!(messages.len(), 3 + 3 * 66);
1242 for (saved, dump) in state.saved.iter().zip(messages[3..].chunks_exact(66)) {
1243 assert_eq!(
1244 dump[0],
1245 format!(
1246 "[ERROR] DIAG_PAGE_HASH_BAD_BEGIN idx={} gpa={:#x} shim_hash={} expected_hash={} len=0x1000",
1247 saved.page_idx,
1248 saved.gpa,
1249 HexBytes(&saved.shim_hash),
1250 HexBytes(&saved.expected_hash),
1251 )
1252 );
1253 for (line_idx, line) in dump[1..65].iter().enumerate() {
1254 assert_eq!(
1255 *line,
1256 format!(
1257 "[ERROR] DIAG_PAGE_HASH_BAD_LINE idx={} gpa={:#x} offset={:#06x} bytes={}",
1258 saved.page_idx,
1259 saved.gpa,
1260 line_idx * 64,
1261 "ab".repeat(64),
1262 )
1263 );
1264 }
1265 assert_eq!(
1266 dump[65],
1267 format!(
1268 "[ERROR] DIAG_PAGE_HASH_BAD_END idx={} gpa={:#x}",
1269 saved.page_idx, saved.gpa,
1270 )
1271 );
1272 }
1273 }
1274
1275 #[test]
1276 fn shared_scan_counts_corruption_beyond_bitmap_and_dump_limits() {
1277 let mut state = expected_zero_pages(DIAG_MAX_HASH_PAGES + 4);
1278 state.seen = (DIAG_MAX_HASH_PAGES - 1) as u32;
1279 state.record(0x1000000, &[1; 4 * DIAG_PAGE_SIZE]);
1280 assert_eq!(state.seen as usize, DIAG_MAX_HASH_PAGES + 3);
1281 assert_eq!(state.bad, 4);
1282 assert_eq!(state.saved_count as usize, DIAG_MAX_SAVED_BAD_PAGES);
1283 assert_eq!(state.bitmap[DIAG_HASH_BITMAP_WORDS - 1], 1 << 63);
1284 assert!(!state.overflow);
1285 }
1286
1287 #[test]
1288 fn shared_scan_handles_missing_or_short_expected_hashes() {
1289 let mut state = DiagPerPageState::new_const();
1290 state.record(0x1000, &[0; DIAG_PAGE_SIZE]);
1291 assert_eq!(state.bad, 0);
1292 assert_eq!(state.saved_count, 0);
1293
1294 let mut state = expected_zero_pages(1);
1295 state.record(0x1000, &[0; 2 * DIAG_PAGE_SIZE]);
1296 assert_eq!(state.seen, 2);
1297 assert_eq!(state.bad, 0);
1298 assert_eq!(state.saved_count, 0);
1299 assert!(state.overflow);
1300 }
1301
1302 #[test]
1303 fn pending_chunks_match_conversion_boundaries() {
1304 let range = MemoryRange::new(0x1ff000..0x601000);
1305 let chunks: Vec<_> = pending_import_chunks(range).collect();
1306 assert_eq!(
1307 chunks,
1308 [
1309 MemoryRange::new(0x1ff000..0x200000),
1310 MemoryRange::new(0x200000..0x400000),
1311 MemoryRange::new(0x400000..0x600000),
1312 MemoryRange::new(0x600000..0x601000),
1313 ]
1314 );
1315 assert_eq!(chunks.iter().map(|r| r.len()).sum::<u64>(), range.len());
1316 assert_eq!(
1317 pending_import_chunks(MemoryRange::new(0x1000..0x1000)).count(),
1318 0
1319 );
1320 }
1321}