1#![cfg(target_os = "linux")]
7
8mod init;
9mod mapping;
10mod registrar;
11
12pub use init::BootInit;
13pub use init::Init;
14pub use init::MemoryMappings;
15pub use init::init;
16
17use aarch64defs::rsi::CcaMemPermIndex;
18use cvm_tracing::CVM_ALLOWED;
19use guestmem::GuestMemoryBackingError;
20use guestmem::PAGE_SIZE;
21use guestmem::ranges::PagedRange;
22use hcl::GuestVtl;
23use hcl::ioctl::AcceptPagesError;
24use hcl::ioctl::ApplyVtlProtectionsError;
25use hcl::ioctl::Mshv;
26use hcl::ioctl::MshvHvcall;
27use hcl::ioctl::MshvVtl;
28use hv1_structs::VtlArray;
29use hvdef::HV_MAP_GPA_PERMISSIONS_ALL;
30use hvdef::HV_MAP_GPA_PERMISSIONS_NONE;
31use hvdef::HV_PAGE_SHIFT;
32use hvdef::HV_PAGE_SIZE;
33use hvdef::HvError;
34use hvdef::HvMapGpaFlags;
35use hvdef::HypercallCode;
36use hvdef::hypercall::AcceptMemoryType;
37use hvdef::hypercall::HostVisibilityType;
38use hvdef::hypercall::HvInputVtl;
39use mapping::GuestMemoryMapping;
40use mapping::GuestValidMemory;
41use memory_range::AlignedSubranges;
42use memory_range::MemoryRange;
43use parking_lot::Mutex;
44use parking_lot::MutexGuard;
45use registrar::RegisterMemory;
46use std::collections::VecDeque;
47use std::sync::Arc;
48use std::sync::atomic::AtomicBool;
49use std::sync::atomic::Ordering;
50use thiserror::Error;
51use virt::IsolationType;
52use virt_mshv_vtl::GpnSource;
53use virt_mshv_vtl::ProtectIsolatedMemory;
54use virt_mshv_vtl::TlbFlushLockAccess;
55use vm_topology::memory::MemoryLayout;
56use x86defs::snp::SevRmpAdjust;
57use x86defs::tdx::GpaVmAttributes;
58use x86defs::tdx::GpaVmAttributesMask;
59use x86defs::tdx::TdgMemPageAttrWriteR8;
60use x86defs::tdx::TdgMemPageGpaAttr;
61
62#[derive(Debug)]
63struct MshvVtlWithPolicy {
64 mshv_vtl: MshvVtl,
65 ignore_registration_failure: bool,
66 shared: bool,
67}
68
69impl RegisterMemory for MshvVtlWithPolicy {
70 fn register_range(&self, range: MemoryRange) -> Result<(), impl 'static + std::error::Error> {
71 match self.mshv_vtl.add_vtl0_memory(range, self.shared) {
72 Ok(()) => Ok(()),
73 Err(err) if self.ignore_registration_failure => {
75 tracing::warn!(
76 CVM_ALLOWED,
77 error = &err as &dyn std::error::Error,
78 "registration failure, could be expected"
79 );
80 Ok(())
81 }
82 Err(err) => Err(err),
83 }
84 }
85}
86
87#[derive(Debug, Error)]
88#[error("failed to register memory with kernel")]
89struct RegistrationError;
90
91struct DefaultVtlPermissions {
99 vtl0: HvMapGpaFlags,
100 vtl1: Option<HvMapGpaFlags>,
101}
102
103impl DefaultVtlPermissions {
104 fn set(&mut self, vtl: GuestVtl, permissions: HvMapGpaFlags) {
105 match vtl {
106 GuestVtl::Vtl0 => self.vtl0 = permissions,
107 GuestVtl::Vtl1 => self.vtl1 = Some(permissions),
108 }
109 }
110}
111
112#[derive(Copy, Clone)]
114enum GpaVtlPermissions {
115 Vbs(HvMapGpaFlags),
116 Snp(SevRmpAdjust),
117 Tdx(TdgMemPageGpaAttr, TdgMemPageAttrWriteR8),
118 Cca(CcaMemPermIndex),
121}
122
123impl GpaVtlPermissions {
124 fn new(isolation: IsolationType, vtl: GuestVtl, protections: HvMapGpaFlags) -> Self {
125 match isolation {
126 IsolationType::None => unreachable!(),
127 IsolationType::Vbs => GpaVtlPermissions::Vbs(protections),
128 IsolationType::Snp => {
129 let mut vtl_permissions = GpaVtlPermissions::Snp(SevRmpAdjust::new());
130 vtl_permissions.set(vtl, protections);
131 vtl_permissions
132 }
133 IsolationType::Tdx => {
134 let mut vtl_permissions =
135 GpaVtlPermissions::Tdx(TdgMemPageGpaAttr::new(), TdgMemPageAttrWriteR8::new());
136 vtl_permissions.set(vtl, protections);
137 vtl_permissions
138 }
139 IsolationType::Cca => {
140 let mut vtl_permissions = GpaVtlPermissions::Cca(CcaMemPermIndex::default());
141 vtl_permissions.set(vtl, protections);
142 vtl_permissions
143 }
144 }
145 }
146
147 fn set(&mut self, vtl: GuestVtl, protections: HvMapGpaFlags) {
148 match self {
149 GpaVtlPermissions::Vbs(flags) => *flags = protections,
150 GpaVtlPermissions::Snp(rmpadjust) => {
151 *rmpadjust = SevRmpAdjust::new()
152 .with_enable_read(protections.readable())
153 .with_enable_write(protections.writable())
154 .with_enable_user_execute(protections.user_executable())
155 .with_enable_kernel_execute(protections.kernel_executable())
156 .with_target_vmpl(match vtl {
157 GuestVtl::Vtl0 => x86defs::snp::Vmpl::Vmpl2.into(),
158 GuestVtl::Vtl1 => x86defs::snp::Vmpl::Vmpl1.into(),
159 });
160 }
161 GpaVtlPermissions::Tdx(attributes, mask) => {
162 let vm_attributes = GpaVmAttributes::new()
163 .with_valid(true)
164 .with_read(protections.readable())
165 .with_write(protections.writable())
166 .with_kernel_execute(protections.kernel_executable())
167 .with_user_execute(protections.user_executable());
168
169 let (new_attributes, new_mask) = match vtl {
170 GuestVtl::Vtl0 => {
171 let attributes = TdgMemPageGpaAttr::new().with_l2_vm1(vm_attributes);
172 let mask = TdgMemPageAttrWriteR8::new()
173 .with_l2_vm1(GpaVmAttributesMask::ALL_CHANGED);
174 (attributes, mask)
175 }
176 GuestVtl::Vtl1 => {
177 let attributes = TdgMemPageGpaAttr::new().with_l2_vm2(vm_attributes);
178 let mask = TdgMemPageAttrWriteR8::new()
179 .with_l2_vm2(GpaVmAttributesMask::ALL_CHANGED);
180 (attributes, mask)
181 }
182 };
183
184 *attributes = new_attributes;
185 *mask = new_mask;
186 }
187 GpaVtlPermissions::Cca(_index) => {
188 tracing::debug!("cca: GpaVtlPermissions::set is doing nothing now");
189 }
190 }
191 }
192}
193
194#[derive(Debug, Error)]
196#[error("failed to modify gpa visibility, elements successfully processed {processed}")]
197pub struct ModifyGpaVisibilityError {
198 source: HvError,
199 processed: usize,
200}
201
202#[derive(Default)]
203struct MemoryAcceptorFlags {
204 tdx_page_release_required: bool,
205}
206
207pub struct MemoryAcceptor {
213 mshv_hvcall: MshvHvcall,
214 mshv_vtl: MshvVtl,
215 isolation: IsolationType,
216 flags: MemoryAcceptorFlags,
217}
218
219impl MemoryAcceptor {
220 pub fn new(isolation: IsolationType) -> Result<Self, hcl::ioctl::Error> {
222 let mshv = Mshv::new()?;
223 let mshv_vtl = mshv.create_vtl()?;
224 let mshv_hvcall = MshvHvcall::new()?;
225 mshv_hvcall.set_allowed_hypercalls(&[
226 HypercallCode::HvCallAcceptGpaPages,
227 HypercallCode::HvCallModifySparseGpaPageHostVisibility,
228 HypercallCode::HvCallModifyVtlProtectionMask,
229 ]);
230
231 let mut flags = MemoryAcceptorFlags::default();
232
233 if isolation == IsolationType::Tdx {
234 let config_flags = mshv_vtl.tdx_get_config_flags();
237 if config_flags.tdx_connect() {
238 assert!(
239 config_flags.page_release(),
240 "TDX Connect enabled but CONFIG_FLAGS.page_release is not set",
241 );
242
243 flags.tdx_page_release_required = true;
244 }
245 }
246
247 Ok(Self {
249 mshv_hvcall,
250 mshv_vtl,
251 isolation,
252 flags,
253 })
254 }
255
256 pub fn accept_lower_vtl_pages(&self, range: MemoryRange) -> Result<(), AcceptPagesError> {
258 match self.isolation {
259 IsolationType::None => unreachable!(),
260 IsolationType::Vbs => self
261 .mshv_hvcall
262 .accept_gpa_pages(range, AcceptMemoryType::RAM),
263 IsolationType::Snp => {
264 self.mshv_vtl
265 .pvalidate_pages(range, true, false)
266 .map_err(|err| AcceptPagesError::Snp {
267 failed_operation: err,
268 range,
269 })
270 }
271 IsolationType::Tdx => {
272 let attributes = TdgMemPageGpaAttr::new().with_l2_vm1(GpaVmAttributes::FULL_ACCESS);
273 let mask =
274 TdgMemPageAttrWriteR8::new().with_l2_vm1(GpaVmAttributesMask::ALL_CHANGED);
275
276 self.mshv_vtl
277 .tdx_accept_pages(range, Some((attributes, mask)))
278 .map_err(|err| AcceptPagesError::Tdx { error: err, range })
279 }
280 IsolationType::Cca => {
281 Ok(())
283 }
284 }
285 }
286
287 fn unaccept_lower_vtl_pages(&self, range: MemoryRange) {
288 match self.isolation {
289 IsolationType::None => unreachable!(),
290 IsolationType::Vbs => {
291 }
293 IsolationType::Snp => {
294 for lower_vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
301 self.apply_protections(range, lower_vtl, HV_MAP_GPA_PERMISSIONS_NONE)
302 .unwrap();
303 }
304 self.mshv_vtl.pvalidate_pages(range, false, false).unwrap()
305 }
306
307 IsolationType::Tdx => {
308 if self.flags.tdx_page_release_required {
309 self.mshv_vtl
310 .tdx_release_pages(range)
311 .unwrap_or_else(|e| panic!("Failed to release memory range {range}: {e}"));
312 }
313 }
314 IsolationType::Cca => {
315 }
317 }
318 }
319
320 pub fn modify_gpa_visibility(
322 &self,
323 host_visibility: HostVisibilityType,
324 gpns: &[u64],
325 ) -> Result<(), ModifyGpaVisibilityError> {
326 self.mshv_hvcall
327 .modify_gpa_visibility(host_visibility, gpns)
328 .map_err(|(e, processed)| ModifyGpaVisibilityError {
329 source: e,
330 processed,
331 })
332 }
333
334 pub fn apply_initial_lower_vtl_protections(
338 &self,
339 range: MemoryRange,
340 ) -> Result<(), ApplyVtlProtectionsError> {
341 self.apply_protections(range, GuestVtl::Vtl0, HV_MAP_GPA_PERMISSIONS_ALL)
342 }
343
344 fn apply_protections(
345 &self,
346 range: MemoryRange,
347 vtl: GuestVtl,
348 flags: HvMapGpaFlags,
349 ) -> Result<(), ApplyVtlProtectionsError> {
350 let permissions = GpaVtlPermissions::new(self.isolation, vtl, flags);
351
352 match permissions {
353 GpaVtlPermissions::Vbs(flags) => {
354 assert_ne!(vtl, GuestVtl::Vtl0);
357
358 self.mshv_hvcall
359 .modify_vtl_protection_mask(range, flags, HvInputVtl::from(vtl))
360 }
361 GpaVtlPermissions::Snp(rmpadjust) => {
362 self.mshv_vtl
365 .rmpadjust_pages(range, rmpadjust, false)
366 .map_err(|err| ApplyVtlProtectionsError::Snp {
367 failed_operation: err,
368 range,
369 permissions: rmpadjust,
370 vtl: vtl.into(),
371 })
372 }
373 GpaVtlPermissions::Tdx(attributes, mask) => {
374 self.mshv_vtl
377 .tdx_set_page_attributes(range, attributes, mask)
378 .map_err(|err| ApplyVtlProtectionsError::Tdx {
379 error: err,
380 range,
381 permissions: attributes,
382 vtl: vtl.into(),
383 })
384 }
385 GpaVtlPermissions::Cca(_index) => {
386 self.mshv_vtl.rsi_set_mem_perm(vtl, &range).map_err(|_err| {
387 ApplyVtlProtectionsError::Cca {
388 range,
389 vtl: vtl.into(),
390 }
391 })
392 }
393 }
394 }
395}
396
397pub struct HardwareIsolatedMemoryProtector {
399 inner: Mutex<HardwareIsolatedMemoryProtectorInner>,
401 layout: MemoryLayout,
402 acceptor: Arc<MemoryAcceptor>,
403 vtl0: Arc<GuestMemoryMapping>,
404 vtl1_protections_enabled: AtomicBool,
405 vp_count: u32,
407}
408
409struct HardwareIsolatedMemoryProtectorInner {
410 valid_encrypted: Arc<GuestValidMemory>,
411 valid_shared: Arc<GuestValidMemory>,
412 encrypted: Arc<GuestMemoryMapping>,
413 default_vtl_permissions: DefaultVtlPermissions,
414 overlay_pages: VtlArray<Vec<OverlayPage>, 2>,
415 locked_pages: VtlArray<Vec<Box<[u64]>>, 2>,
416}
417
418struct OverlayPage {
419 gpn: u64,
420 previous_permissions: HvMapGpaFlags,
421 overlay_permissions: HvMapGpaFlags,
422 ref_count: u16,
423 gpn_source: GpnSource,
424}
425
426impl HardwareIsolatedMemoryProtector {
427 pub fn new(
432 valid_encrypted: Arc<GuestValidMemory>,
433 valid_shared: Arc<GuestValidMemory>,
434 encrypted: Arc<GuestMemoryMapping>,
435 vtl0: Arc<GuestMemoryMapping>,
436 layout: MemoryLayout,
437 acceptor: Arc<MemoryAcceptor>,
438 vp_count: u32,
439 ) -> Self {
440 Self {
441 inner: Mutex::new(HardwareIsolatedMemoryProtectorInner {
442 valid_encrypted,
443 valid_shared,
444 encrypted,
445 default_vtl_permissions: DefaultVtlPermissions {
448 vtl0: HV_MAP_GPA_PERMISSIONS_ALL,
449 vtl1: None,
450 },
451 overlay_pages: VtlArray::from_fn(|_| Vec::new()),
452 locked_pages: VtlArray::from_fn(|_| Vec::new()),
453 }),
454 layout,
455 acceptor,
456 vtl0,
457 vtl1_protections_enabled: AtomicBool::new(false),
458 vp_count,
459 }
460 }
461
462 fn apply_protections(
466 &self,
467 range: MemoryRange,
468 target_vtl: GuestVtl,
469 protections: HvMapGpaFlags,
470 gpn_source: GpnSource,
471 ) -> Result<(), ApplyVtlProtectionsError> {
472 if gpn_source == GpnSource::GuestMemory && target_vtl == GuestVtl::Vtl0 {
473 self.vtl0.update_permission_bitmaps(range, protections);
475 }
476
477 self.acceptor
478 .apply_protections(range, target_vtl, protections)
479 }
480
481 fn query_lower_vtl_permissions(
486 &self,
487 vtl: GuestVtl,
488 gpn: u64,
489 ) -> Result<HvMapGpaFlags, HvError> {
490 if !self.is_in_guest_memory(gpn) {
491 return Err(HvError::OperationDenied);
492 }
493
494 let res = match vtl {
495 GuestVtl::Vtl0 => self
496 .vtl0
497 .query_access_permission(gpn)
498 .unwrap_or(HV_MAP_GPA_PERMISSIONS_ALL),
499 GuestVtl::Vtl1 => HV_MAP_GPA_PERMISSIONS_ALL,
500 };
501
502 Ok(res)
503 }
504
505 fn check_gpn_not_locked(
506 &self,
507 inner: &MutexGuard<'_, HardwareIsolatedMemoryProtectorInner>,
508 vtl: GuestVtl,
509 gpn: u64,
510 ) -> Result<(), HvError> {
511 if inner.locked_pages[vtl].iter().flatten().any(|x| *x == gpn)
516 && !inner.overlay_pages[vtl].iter().any(|p| p.gpn == gpn)
517 {
518 return Err(HvError::OperationDenied);
519 }
520 Ok(())
521 }
522
523 fn is_in_guest_memory(&self, gpn: u64) -> bool {
525 let gpa = gpn << HV_PAGE_SHIFT;
526 self.layout.ram().iter().any(|r| r.range.contains_addr(gpa))
527 }
528}
529
530impl ProtectIsolatedMemory for HardwareIsolatedMemoryProtector {
531 fn change_host_visibility(
532 &self,
533 vtl: GuestVtl,
534 shared: bool,
535 gpns: &[u64],
536 tlb_access: &mut dyn TlbFlushLockAccess,
537 ) -> Result<(), (HvError, usize)> {
538 let inner = self.inner.lock();
539
540 for &gpn in gpns {
541 if !self.is_in_guest_memory(gpn) {
543 return Err((HvError::OperationDenied, 0));
544 }
545
546 self.check_gpn_not_locked(&inner, vtl, gpn)
548 .map_err(|x| (x, 0))?;
549
550 if shared && inner.overlay_pages[vtl].iter().any(|p| p.gpn == gpn) {
552 return Err((HvError::OperationDenied, 0));
553 }
554 }
555
556 let orig_gpns = gpns;
560 let mut failed_vtl_permission_index = None;
561 let gpns = gpns
562 .iter()
563 .copied()
564 .enumerate()
565 .take_while(|&(index, gpn)| {
566 if vtl == GuestVtl::Vtl0 && shared && self.vtl1_protections_enabled() {
567 let permissions = self
568 .vtl0
569 .query_access_permission(gpn)
570 .expect("vtl 1 protections enabled, vtl permissions should be tracked");
571 if !permissions.readable() || !permissions.writable() {
572 failed_vtl_permission_index = Some(index);
573 false
574 } else {
575 true
576 }
577 } else {
578 true
579 }
580 })
581 .filter_map(|(_, gpn)| {
582 if inner.valid_shared.check_valid(gpn) != shared {
583 Some(gpn)
584 } else {
585 None
586 }
587 })
588 .collect::<Vec<_>>();
589
590 tracing::debug!(
591 orig = orig_gpns.len(),
592 len = gpns.len(),
593 first = gpns.first(),
594 shared,
595 "change vis"
596 );
597
598 let ranges = PagedRange::new(0, gpns.len() * PagedRange::PAGE_SIZE, &gpns)
599 .unwrap()
600 .ranges()
601 .map(|r| r.map(|r| MemoryRange::new(r.start..r.end)))
602 .collect::<Result<Vec<_>, _>>()
603 .unwrap(); let clear_bitmap = if shared {
607 &inner.valid_encrypted
608 } else {
609 &inner.valid_shared
610 };
611
612 for &range in &ranges {
613 if shared && vtl == GuestVtl::Vtl0 {
614 self.vtl0
620 .update_permission_bitmaps(range, HV_MAP_GPA_PERMISSIONS_NONE);
621 }
622
623 clear_bitmap.update_valid(range, false);
624 }
625
626 guestmem::rcu().synchronize_blocking();
633
634 if let IsolationType::Snp = self.acceptor.isolation {
635 tlb_access.flush_entire();
639 }
640
641 if shared {
642 for &range in &ranges {
644 self.acceptor.unaccept_lower_vtl_pages(range);
645 }
646 }
647
648 let host_visibility = if shared {
650 HostVisibilityType::SHARED
651 } else {
652 HostVisibilityType::PRIVATE
653 };
654
655 let (result, ranges) = match self.acceptor.modify_gpa_visibility(host_visibility, &gpns) {
656 Ok(()) => {
657 (
660 match failed_vtl_permission_index {
661 Some(index) => Err((HvError::AccessDenied, index)),
662 None => Ok(()),
663 },
664 ranges,
665 )
666 }
667 Err(err) => {
668 if shared {
669 panic!(
673 "the hypervisor refused to transition pages to shared, we cannot safely roll back: {:?}",
674 err
675 );
676 }
677
678 let (successful_gpns, failed_gpns) = gpns.split_at(err.processed);
681 let ranges = PagedRange::new(
682 0,
683 successful_gpns.len() * PagedRange::PAGE_SIZE,
684 successful_gpns,
685 )
686 .unwrap()
687 .ranges()
688 .map(|r| r.map(|r| MemoryRange::new(r.start..r.end)))
689 .collect::<Result<Vec<_>, _>>()
690 .expect("previous gpns was already checked");
691
692 let rollback_ranges =
695 PagedRange::new(0, failed_gpns.len() * PagedRange::PAGE_SIZE, failed_gpns)
696 .unwrap()
697 .ranges()
698 .map(|r| r.map(|r| MemoryRange::new(r.start..r.end)))
699 .collect::<Result<Vec<_>, _>>()
700 .expect("previous gpns was already checked");
701
702 for &range in &rollback_ranges {
703 clear_bitmap.update_valid(range, true);
704 }
705
706 let failed_index = orig_gpns
709 .iter()
710 .position(|gpn| *gpn == failed_gpns[0])
711 .expect("failed gpn should be present in the list");
712
713 (Err((err.source, failed_index)), ranges)
714 }
715 };
716
717 if !shared {
718 for &range in &ranges {
720 self.acceptor
721 .accept_lower_vtl_pages(range)
722 .expect("everything should be in a state where we can accept VTL0 pages");
723
724 if self.acceptor.isolation == IsolationType::Snp {
728 inner.encrypted.zero_range(range).expect("VTL 2 should have access to lower VTL memory, the page should be accepted, there should be no vtl protections yet.")
729 }
730 }
731 }
732
733 let set_bitmap = if shared {
735 &inner.valid_shared
736 } else {
737 &inner.valid_encrypted
738 };
739 for &range in &ranges {
740 set_bitmap.update_valid(range, true);
741 }
742
743 if !shared {
744 for &range in &ranges {
748 self.apply_protections(
750 range,
751 GuestVtl::Vtl0,
752 inner.default_vtl_permissions.vtl0,
753 GpnSource::GuestMemory,
754 )
755 .expect("should be able to apply default protections");
756
757 if let Some(vtl1_protections) = inner.default_vtl_permissions.vtl1 {
758 self.apply_protections(
759 range,
760 GuestVtl::Vtl1,
761 vtl1_protections,
762 GpnSource::GuestMemory,
763 )
764 .expect("everything should be in a state where we can apply VTL protections");
765 }
766 }
767 }
768
769 result
772 }
773
774 fn query_host_visibility(
775 &self,
776 gpns: &[u64],
777 host_visibility: &mut [HostVisibilityType],
778 ) -> Result<(), (HvError, usize)> {
779 for (i, &gpn) in gpns.iter().enumerate() {
781 if !self.is_in_guest_memory(gpn) {
782 return Err((HvError::OperationDenied, i));
783 }
784 }
785
786 let inner = self.inner.lock();
787
788 for (gpn, host_vis) in gpns.iter().zip(host_visibility.iter_mut()) {
790 *host_vis = if inner.valid_shared.check_valid(*gpn) {
791 HostVisibilityType::SHARED
792 } else {
793 HostVisibilityType::PRIVATE
794 };
795 }
796 Ok(())
797 }
798
799 fn default_vtl0_protections(&self) -> HvMapGpaFlags {
800 self.inner.lock().default_vtl_permissions.vtl0
801 }
802
803 fn change_default_vtl_protections(
804 &self,
805 target_vtl: GuestVtl,
806 vtl_protections: HvMapGpaFlags,
807 tlb_access: &mut dyn TlbFlushLockAccess,
808 ) -> Result<(), HvError> {
809 let mut inner = self.inner.lock();
816
817 inner
818 .default_vtl_permissions
819 .set(target_vtl, vtl_protections);
820
821 let mut ranges = Vec::new();
822 for ram_range in self.layout.ram().iter() {
823 let mut protect_start = ram_range.range.start();
824 let mut page_count = 0;
825
826 for gpn in
827 ram_range.range.start() / PAGE_SIZE as u64..ram_range.range.end() / PAGE_SIZE as u64
828 {
829 if inner.valid_encrypted.check_valid(gpn) {
835 self.check_gpn_not_locked(&inner, target_vtl, gpn)?;
836 page_count += 1;
837 } else {
838 if page_count > 0 {
839 let end_address = protect_start + (page_count * PAGE_SIZE as u64);
840 ranges.push(MemoryRange::new(protect_start..end_address));
841 }
842 protect_start = (gpn + 1) * PAGE_SIZE as u64;
843 page_count = 0;
844 }
845 }
846
847 if page_count > 0 {
848 let end_address = protect_start + (page_count * PAGE_SIZE as u64);
849 ranges.push(MemoryRange::new(protect_start..end_address));
850 }
851 }
852
853 tracing::trace!("Applying default vtl protections.");
854
855 let protect_subrange = move |subrange: MemoryRange| {
856 self.apply_protections(
857 subrange,
858 target_vtl,
859 vtl_protections,
860 GpnSource::GuestMemory,
861 )
862 };
863
864 let mut protect_ranges = Vec::new();
867 for source_range in ranges {
868 let mut range_queue = VecDeque::new();
869 range_queue.push_back(source_range);
870
871 'outer: while let Some(range) = range_queue.pop_front() {
872 for overlay_page in inner.overlay_pages[target_vtl].iter_mut() {
873 let overlay_addr = overlay_page.gpn * HV_PAGE_SIZE;
874 if range.contains_addr(overlay_addr) {
875 overlay_page.previous_permissions = vtl_protections;
878 let (left, right_with_overlay) =
880 range.split_at_offset(range.offset_of(overlay_addr).unwrap());
881 let (overlay, right) = right_with_overlay.split_at_offset(HV_PAGE_SIZE);
882 debug_assert_eq!(overlay.start_4k_gpn(), overlay_page.gpn);
883 debug_assert_eq!(overlay.len(), HV_PAGE_SIZE);
884 if !left.is_empty() {
885 range_queue.push_back(left);
886 }
887 if !right.is_empty() {
888 range_queue.push_back(right);
889 }
890 continue 'outer;
891 }
892 }
893
894 protect_ranges.push(range);
897 }
898 }
899
900 parallelize_mem_op(&protect_ranges, self.vp_count, protect_subrange)
901 .expect("applying default VTL protections should not fail");
902
903 tracing::trace!("Finished applying default vtl protections.");
904
905 guestmem::rcu().synchronize_blocking();
908
909 tlb_access.flush(GuestVtl::Vtl0);
912 tlb_access.set_wait_for_tlb_locks(target_vtl);
913
914 Ok(())
915 }
916
917 fn change_vtl_protections(
918 &self,
919 target_vtl: GuestVtl,
920 gpns: &[u64],
921 protections: HvMapGpaFlags,
922 tlb_access: &mut dyn TlbFlushLockAccess,
923 ) -> Result<(), (HvError, usize)> {
924 let inner = self.inner.lock();
929
930 for &gpn in gpns {
932 if !self.is_in_guest_memory(gpn) {
933 return Err((HvError::OperationDenied, 0));
934 }
935
936 self.check_gpn_not_locked(&inner, target_vtl, gpn)
938 .map_err(|x| (x, 0))?;
939
940 if inner.overlay_pages[target_vtl].iter().any(|p| p.gpn == gpn) {
942 return Err((HvError::OperationDenied, 0));
943 }
944 }
945
946 if gpns.iter().any(|&gpn| inner.valid_shared.check_valid(gpn)) {
948 return Err((HvError::OperationDenied, 0));
949 }
950
951 let ranges = PagedRange::new(0, gpns.len() * PagedRange::PAGE_SIZE, gpns)
952 .unwrap()
953 .ranges()
954 .map(|r| r.map(|r| MemoryRange::new(r.start..r.end)))
955 .collect::<Result<Vec<_>, _>>()
956 .unwrap(); for range in ranges {
959 self.apply_protections(range, target_vtl, protections, GpnSource::GuestMemory)
960 .unwrap();
961 }
962
963 guestmem::rcu().synchronize_blocking();
966
967 tlb_access.flush(GuestVtl::Vtl0);
972 tlb_access.set_wait_for_tlb_locks(target_vtl);
973
974 Ok(())
975 }
976
977 fn register_overlay_page(
978 &self,
979 vtl: GuestVtl,
980 gpn: u64,
981 gpn_source: GpnSource,
982 check_perms: HvMapGpaFlags,
983 new_perms: Option<HvMapGpaFlags>,
984 tlb_access: &mut dyn TlbFlushLockAccess,
985 ) -> Result<(), HvError> {
986 let mut inner = self.inner.lock();
987
988 if let Some(registered) = inner.overlay_pages[vtl].iter_mut().find(|p| p.gpn == gpn) {
993 let needed_perms = new_perms.unwrap_or(check_perms);
994 if registered.overlay_permissions.into_bits() | needed_perms.into_bits()
995 != registered.overlay_permissions.into_bits()
996 {
997 return Err(HvError::OperationDenied);
998 }
999 registered.ref_count += 1;
1000 return Ok(());
1001 }
1002
1003 let current_perms = match gpn_source {
1004 GpnSource::GuestMemory => {
1005 let current_perms = self.query_lower_vtl_permissions(vtl, gpn)?;
1007 if current_perms.into_bits() | check_perms.into_bits() != current_perms.into_bits()
1008 {
1009 return Err(HvError::OperationDenied);
1010 }
1011
1012 if inner.valid_shared.check_valid(gpn) {
1014 return Err(HvError::OperationDenied);
1015 }
1016
1017 current_perms
1018 }
1019 GpnSource::Dma => {
1020 if self.is_in_guest_memory(gpn) {
1021 return Err(HvError::OperationDenied);
1023 }
1024
1025 HV_MAP_GPA_PERMISSIONS_NONE
1026 }
1027 };
1028
1029 self.check_gpn_not_locked(&inner, vtl, gpn)?;
1031
1032 if let Some(new_perms) = new_perms {
1034 self.apply_protections(
1035 MemoryRange::from_4k_gpn_range(gpn..gpn + 1),
1036 vtl,
1037 new_perms,
1038 gpn_source,
1039 )
1040 .map_err(|_| HvError::OperationDenied)?;
1041 }
1042
1043 inner.overlay_pages[vtl].push(OverlayPage {
1045 gpn,
1046 previous_permissions: current_perms,
1047 overlay_permissions: new_perms.unwrap_or(current_perms),
1048 ref_count: 1,
1049 gpn_source,
1050 });
1051
1052 guestmem::rcu().synchronize_blocking();
1055
1056 tlb_access.flush(vtl);
1061 tlb_access.set_wait_for_tlb_locks(vtl);
1062
1063 Ok(())
1064 }
1065
1066 fn unregister_overlay_page(
1067 &self,
1068 vtl: GuestVtl,
1069 gpn: u64,
1070 tlb_access: &mut dyn TlbFlushLockAccess,
1071 ) -> Result<(), HvError> {
1072 let mut inner = self.inner.lock();
1073 let overlay_pages = &mut inner.overlay_pages[vtl];
1074
1075 let index = overlay_pages
1077 .iter()
1078 .position(|p| p.gpn == gpn)
1079 .ok_or(HvError::OperationDenied)?;
1080
1081 if overlay_pages[index].ref_count > 1 {
1086 overlay_pages[index].ref_count -= 1;
1087 return Ok(());
1088 }
1089
1090 self.apply_protections(
1092 MemoryRange::from_4k_gpn_range(gpn..gpn + 1),
1093 vtl,
1094 overlay_pages[index].previous_permissions,
1095 overlay_pages[index].gpn_source,
1096 )
1097 .map_err(|_| HvError::OperationDenied)?;
1098
1099 overlay_pages.remove(index);
1101
1102 guestmem::rcu().synchronize_blocking();
1105
1106 tlb_access.flush(vtl);
1111 tlb_access.set_wait_for_tlb_locks(vtl);
1112 Ok(())
1113 }
1114
1115 fn is_overlay_page(&self, vtl: GuestVtl, gpn: u64) -> bool {
1116 self.inner.lock().overlay_pages[vtl]
1117 .iter()
1118 .any(|p| p.gpn == gpn)
1119 }
1120
1121 fn lock_gpns(&self, vtl: GuestVtl, gpns: &[u64]) -> Result<(), GuestMemoryBackingError> {
1122 self.inner.lock().locked_pages[vtl].push(gpns.to_vec().into_boxed_slice());
1129 Ok(())
1130 }
1131
1132 fn unlock_gpns(&self, vtl: GuestVtl, gpns: &[u64]) {
1133 let mut inner = self.inner.lock();
1134 let locked_pages = &mut inner.locked_pages[vtl];
1135 for (i, w) in locked_pages.iter().enumerate() {
1136 if **w == *gpns {
1137 locked_pages.swap_remove(i);
1138 return;
1139 }
1140 }
1141
1142 panic!("Tried to unlock pages that were not locked");
1148 }
1149
1150 fn set_vtl1_protections_enabled(&self) {
1151 self.vtl1_protections_enabled.store(true, Ordering::Relaxed);
1152 }
1153
1154 fn vtl1_protections_enabled(&self) -> bool {
1155 self.vtl1_protections_enabled.load(Ordering::Relaxed)
1156 }
1157}
1158
1159pub(crate) fn parallelize_mem_op<E>(
1160 source_ranges: &[MemoryRange],
1161 vp_count: u32,
1162 op: impl Fn(MemoryRange) -> Result<(), E> + Send + Sync + Copy,
1163) -> Result<(), E>
1164where
1165 E: Send,
1166{
1167 const LARGE_PAGE_SIZE: u64 = 2 * 1024 * 1024;
1168 const MAX_RANGE_LEN: u64 = 2 * 1024 * 1024 * 1024;
1171
1172 let worker_count = vp_count.saturating_sub(1).max(1);
1173
1174 let total_len = source_ranges
1175 .iter()
1176 .fold(0_u64, |len, range| len.saturating_add(range.len()));
1177 let target_range_len = total_len
1178 .div_ceil(u64::from(worker_count))
1179 .clamp(LARGE_PAGE_SIZE, MAX_RANGE_LEN)
1180 .next_multiple_of(LARGE_PAGE_SIZE)
1181 .min(MAX_RANGE_LEN);
1182
1183 let ranges: Vec<_> = source_ranges
1186 .iter()
1187 .flat_map(|range| AlignedSubranges::new(*range).with_max_range_len(target_range_len))
1188 .collect();
1189 if ranges.is_empty() {
1190 return Ok(());
1191 }
1192
1193 std::thread::scope(|scope| {
1194 let chunk_len = ranges.len().div_ceil(worker_count as usize);
1197 let workers: Vec<_> = ranges
1198 .chunks(chunk_len)
1199 .map(|chunk| {
1200 scope.spawn(move || {
1201 for &range in chunk {
1202 op(range)?;
1203 }
1204 Ok::<(), E>(())
1205 })
1206 })
1207 .collect();
1208
1209 for worker in workers {
1210 worker.join().expect("memory range worker panicked")?;
1211 }
1212
1213 Ok(())
1214 })
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219 use super::*;
1220 use std::convert::Infallible;
1221
1222 #[test]
1223 fn parallelize_mem_op_covers_source_ranges() {
1224 const MB: u64 = 1024 * 1024;
1225
1226 let source_ranges = [
1227 MemoryRange::new(0x1000..7 * MB + 0x1000),
1228 MemoryRange::new(10 * MB..11 * MB),
1229 ];
1230 let completed = Mutex::new(Vec::new());
1231
1232 parallelize_mem_op(&source_ranges, 5, |range| {
1233 completed.lock().push(range);
1234 Ok::<_, Infallible>(())
1235 })
1236 .unwrap();
1237
1238 let mut completed = completed.into_inner();
1239 completed.sort_by_key(|range| range.start());
1240
1241 assert!(
1244 memory_range::flatten_ranges(completed).eq(memory_range::flatten_ranges(source_ranges))
1245 );
1246 }
1247
1248 #[test]
1249 fn parallelize_mem_op_handles_empty_input() {
1250 parallelize_mem_op(&[], 0, |_| Ok::<_, Infallible>(())).unwrap();
1251 }
1252
1253 #[test]
1254 fn parallelize_mem_op_fewer_workers_than_ranges() {
1255 const MB: u64 = 1024 * 1024;
1256 const PAGE: u64 = 0x1000;
1257
1258 let source_ranges = [
1261 MemoryRange::new(PAGE..3 * PAGE),
1263 MemoryRange::new(2 * MB..2 * MB + PAGE),
1265 MemoryRange::new(4 * MB - PAGE..4 * MB + PAGE),
1267 MemoryRange::new(6 * MB..8 * MB),
1269 MemoryRange::new(10 * MB..12 * MB + PAGE),
1271 MemoryRange::new(14 * MB + PAGE..18 * MB),
1273 ];
1274 let completed = Mutex::new(Vec::new());
1275
1276 parallelize_mem_op(&source_ranges, 4, |range| {
1278 completed.lock().push(range);
1279 Ok::<_, Infallible>(())
1280 })
1281 .unwrap();
1282
1283 let mut completed = completed.into_inner();
1284 completed.sort_by_key(|range| range.start());
1285
1286 assert!(
1287 memory_range::flatten_ranges(completed).eq(memory_range::flatten_ranges(source_ranges))
1288 );
1289 }
1290
1291 #[test]
1292 fn parallelize_mem_op_propagates_worker_error() {
1293 let error = parallelize_mem_op(&[MemoryRange::new(0..0x1000)], 2, |_| Err("failed"));
1294
1295 assert_eq!(error, Err("failed"));
1296 }
1297}