@@ -21,7 +21,10 @@ use std::sync::Arc;
2121use std:: sync:: Mutex ;
2222use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
2323
24- use kvm_bindings:: { KVM_MEM_READONLY , kvm_fpu, kvm_regs, kvm_userspace_memory_region} ;
24+ use hyperlight_common:: mem:: { PAGE_SIZE_USIZE , PAGES_IN_BLOCK } ;
25+ use kvm_bindings:: {
26+ KVM_MEM_LOG_DIRTY_PAGES , KVM_MEM_READONLY , kvm_fpu, kvm_regs, kvm_userspace_memory_region,
27+ } ;
2528use kvm_ioctls:: Cap :: UserMemory ;
2629use kvm_ioctls:: { Kvm , VcpuExit , VcpuFd , VmFd } ;
2730use log:: LevelFilter ;
@@ -43,7 +46,8 @@ use super::{
4346use super :: { HyperlightExit , Hypervisor , InterruptHandle , LinuxInterruptHandle , VirtualCPU } ;
4447#[ cfg( gdb) ]
4548use crate :: HyperlightError ;
46- use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags } ;
49+ use crate :: mem:: bitmap:: { bit_index_iterator, new_page_bitmap} ;
50+ use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags , MemoryRegionType } ;
4751use crate :: mem:: ptr:: { GuestPtr , RawPtr } ;
4852use crate :: sandbox:: SandboxConfiguration ;
4953#[ cfg( crashdump) ]
@@ -284,7 +288,7 @@ mod debug {
284288/// A Hypervisor driver for KVM on Linux
285289pub ( crate ) struct KVMDriver {
286290 _kvm : Kvm ,
287- _vm_fd : VmFd ,
291+ vm_fd : VmFd ,
288292 vcpu_fd : VcpuFd ,
289293 entrypoint : u64 ,
290294 orig_rsp : GuestPtr ,
@@ -329,7 +333,7 @@ impl KVMDriver {
329333 userspace_addr : region. host_region . start as u64 ,
330334 flags : match perm_flags {
331335 MemoryRegionFlags :: READ => KVM_MEM_READONLY ,
332- _ => 0 , // normal, RWX
336+ _ => KVM_MEM_LOG_DIRTY_PAGES , // normal, RWX
333337 } ,
334338 } ;
335339 unsafe { vm_fd. set_user_memory_region ( kvm_region) }
@@ -378,7 +382,7 @@ impl KVMDriver {
378382 #[ allow( unused_mut) ]
379383 let mut hv = Self {
380384 _kvm : kvm,
381- _vm_fd : vm_fd,
385+ vm_fd,
382386 vcpu_fd,
383387 entrypoint,
384388 orig_rsp : rsp_gp,
@@ -734,6 +738,45 @@ impl Hypervisor for KVMDriver {
734738 self . interrupt_handle . clone ( )
735739 }
736740
741+ fn get_and_clear_dirty_pages ( & mut self ) -> Result < Vec < u64 > > {
742+ let mut page_indices = vec ! [ ] ;
743+ let mut current_page = 0 ;
744+ // Iterate over all memory regions and get the dirty pages for each region ignoring guard pages which cannot be dirty
745+ for ( i, mem_region) in self . mem_regions . iter ( ) . enumerate ( ) {
746+ let num_pages = mem_region. guest_region . len ( ) / PAGE_SIZE_USIZE ;
747+ let bitmap = match mem_region. flags {
748+ MemoryRegionFlags :: READ => {
749+ // read-only page. It can never be dirty so return zero dirty pages.
750+ new_page_bitmap ( mem_region. guest_region . len ( ) , false ) ?
751+ }
752+ _ => {
753+ if mem_region. region_type == MemoryRegionType :: GuardPage {
754+ // Trying to get dirty pages for a guard page region results in a VMMSysError(2)
755+ new_page_bitmap ( mem_region. guest_region . len ( ) , false ) ?
756+ } else {
757+ // Get the dirty bitmap for the memory region
758+ self . vm_fd
759+ . get_dirty_log ( i as u32 , mem_region. guest_region . len ( ) ) ?
760+ }
761+ }
762+ } ;
763+ for page_idx in bit_index_iterator ( & bitmap) {
764+ page_indices. push ( current_page + page_idx) ;
765+ }
766+ current_page += num_pages;
767+ }
768+
769+ // covert vec of page indices to vec of blocks
770+ let mut res = new_page_bitmap ( current_page * PAGE_SIZE_USIZE , false ) ?;
771+ for page_idx in page_indices {
772+ let block_idx = page_idx / PAGES_IN_BLOCK ;
773+ let bit_idx = page_idx % PAGES_IN_BLOCK ;
774+ res[ block_idx] |= 1 << bit_idx;
775+ }
776+
777+ Ok ( res)
778+ }
779+
737780 #[ cfg( crashdump) ]
738781 fn crashdump_context ( & self ) -> Result < Option < crashdump:: CrashDumpContext > > {
739782 if self . rt_cfg . guest_core_dump {
0 commit comments