@@ -13,12 +13,13 @@ use std::path::Path;
1313use std:: sync:: Arc ;
1414
1515use kvm_bindings:: {
16- KVM_MEM_GUEST_MEMFD , KVM_MEM_LOG_DIRTY_PAGES , KVM_MEMORY_ATTRIBUTE_PRIVATE ,
16+ KVM_MEM_GUEST_MEMFD , KVM_MEM_LOG_DIRTY_PAGES , KVM_MEMORY_ATTRIBUTE_PRIVATE , KVMIO ,
1717 kvm_create_guest_memfd, kvm_memory_attributes, kvm_userspace_memory_region,
18- kvm_userspace_memory_region2,
1918} ;
2019use kvm_ioctls:: { Cap , VmFd } ;
2120use vmm_sys_util:: eventfd:: EventFd ;
21+ use vmm_sys_util:: ioctl:: ioctl_with_ref;
22+ use vmm_sys_util:: { ioctl_ioc_nr, ioctl_iow_nr} ;
2223
2324pub use crate :: arch:: { ArchVm as Vm , ArchVmError , VmState } ;
2425use crate :: arch:: { VM_TYPE_FOR_SECRET_FREEDOM , host_page_size} ;
@@ -73,6 +74,24 @@ pub enum VmError {
7374 SetMemoryAttributes ( kvm_ioctls:: Error ) ,
7475}
7576
77+ // Upstream `kvm_userspace_memory_region2` definition does not include `userfault_bitmap` field yet.
78+ // TODO: revert to `kvm_userspace_memory_region2` from kvm-bindings
79+ #[ allow( non_camel_case_types) ]
80+ #[ repr( C ) ]
81+ #[ derive( Debug , Default , Copy , Clone , PartialEq ) ]
82+ struct kvm_userspace_memory_region2 {
83+ slot : u32 ,
84+ flags : u32 ,
85+ guest_phys_addr : u64 ,
86+ memory_size : u64 ,
87+ userspace_addr : u64 ,
88+ guest_memfd_offset : u64 ,
89+ guest_memfd : u32 ,
90+ pad1 : u32 ,
91+ userfault_bitmap : u64 ,
92+ pad2 : [ u64 ; 13 ] ,
93+ }
94+
7695/// Contains Vm functions that are usable across CPU architectures
7796impl Vm {
7897 /// Create a KVM VM
@@ -181,16 +200,75 @@ impl Vm {
181200 pub fn register_memory_regions (
182201 & mut self ,
183202 regions : Vec < GuestRegionMmap > ,
203+ userfault_bitmap_memfd : Option < & File > ,
184204 ) -> Result < ( ) , VmError > {
205+ let addr = match userfault_bitmap_memfd {
206+ Some ( file) => {
207+ let addr = unsafe {
208+ libc:: mmap (
209+ std:: ptr:: null_mut ( ) ,
210+ file. metadata ( ) . unwrap ( ) . len ( ) as usize ,
211+ libc:: PROT_WRITE ,
212+ libc:: MAP_SHARED ,
213+ file. as_raw_fd ( ) ,
214+ 0 ,
215+ )
216+ } ;
217+
218+ if addr == libc:: MAP_FAILED {
219+ panic ! (
220+ "Failed to mmap userfault bitmap file: {}" ,
221+ std:: io:: Error :: last_os_error( )
222+ ) ;
223+ }
224+
225+ Some ( addr as u64 )
226+ }
227+ None => None ,
228+ } ;
229+
185230 for region in regions {
186- self . register_memory_region ( region) ?
231+ self . register_memory_region ( region, addr ) ?
187232 }
188233
189234 Ok ( ( ) )
190235 }
191236
237+ // TODO: remove when userfault support is merged upstream
238+ fn set_user_memory_region2 (
239+ & self ,
240+ user_memory_region2 : kvm_userspace_memory_region2 ,
241+ ) -> Result < ( ) , VmError > {
242+ ioctl_iow_nr ! (
243+ KVM_SET_USER_MEMORY_REGION2 ,
244+ KVMIO ,
245+ 0x49 ,
246+ kvm_userspace_memory_region2
247+ ) ;
248+
249+ let ret = unsafe {
250+ ioctl_with_ref (
251+ self . fd ( ) ,
252+ KVM_SET_USER_MEMORY_REGION2 ( ) ,
253+ & user_memory_region2,
254+ )
255+ } ;
256+ if ret == 0 {
257+ Ok ( ( ) )
258+ } else {
259+ Err ( VmError :: SetUserMemoryRegion ( kvm_ioctls:: Error :: last ( ) ) )
260+ }
261+ }
262+
192263 /// Register a new memory region to this [`Vm`].
193- pub fn register_memory_region ( & mut self , region : GuestRegionMmap ) -> Result < ( ) , VmError > {
264+ pub fn register_memory_region (
265+ & mut self ,
266+ region : GuestRegionMmap ,
267+ userfault_addr : Option < u64 > ,
268+ ) -> Result < ( ) , VmError > {
269+ // TODO: take it from kvm-bindings when merged upstream
270+ const KVM_MEM_USERFAULT : u32 = 1 << 3 ;
271+
194272 let next_slot = self
195273 . guest_memory ( )
196274 . num_regions ( )
@@ -218,6 +296,18 @@ impl Vm {
218296 ( 0 , 0 )
219297 } ;
220298
299+ let userfault_bitmap = match userfault_addr {
300+ Some ( addr) => {
301+ flags |= KVM_MEM_USERFAULT ;
302+
303+ let file_offset_start = region. file_offset ( ) . unwrap ( ) . start ( ) ;
304+ let pages_offset = file_offset_start / ( host_page_size ( ) as u64 ) ;
305+ let bytes_offset = pages_offset / ( u8:: BITS as u64 ) ;
306+ addr + bytes_offset
307+ }
308+ None => 0 ,
309+ } ;
310+
221311 let memory_region = kvm_userspace_memory_region2 {
222312 slot : next_slot,
223313 guest_phys_addr : region. start_addr ( ) . raw_value ( ) ,
@@ -226,18 +316,14 @@ impl Vm {
226316 flags,
227317 guest_memfd,
228318 guest_memfd_offset,
319+ userfault_bitmap,
229320 ..Default :: default ( )
230321 } ;
231322
232323 let new_guest_memory = self . common . guest_memory . insert_region ( Arc :: new ( region) ) ?;
233324
234325 if self . fd ( ) . check_extension ( Cap :: UserMemory2 ) {
235- // SAFETY: We are passing a valid memory region and operate on a valid KVM FD.
236- unsafe {
237- self . fd ( )
238- . set_user_memory_region2 ( memory_region)
239- . map_err ( VmError :: SetUserMemoryRegion ) ?;
240- }
326+ self . set_user_memory_region2 ( memory_region) ?;
241327 } else {
242328 // Something is seriously wrong if we manage to set these fields on a host that doesn't
243329 // even allow creation of guest_memfds!
@@ -417,7 +503,7 @@ pub(crate) mod tests {
417503 pub ( crate ) fn setup_vm_with_memory ( mem_size : usize ) -> ( Kvm , Vm ) {
418504 let ( kvm, mut vm) = setup_vm ( ) ;
419505 let gm = single_region_mem_raw ( mem_size) ;
420- vm. register_memory_regions ( gm) . unwrap ( ) ;
506+ vm. register_memory_regions ( gm, None ) . unwrap ( ) ;
421507 ( kvm, vm)
422508 }
423509
@@ -447,14 +533,14 @@ pub(crate) mod tests {
447533 // Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE
448534 // will result in error.
449535 let gm = single_region_mem_raw ( 0x10 ) ;
450- let res = vm. register_memory_regions ( gm) ;
536+ let res = vm. register_memory_regions ( gm, None ) ;
451537 assert_eq ! (
452538 res. unwrap_err( ) . to_string( ) ,
453539 "Cannot set the memory regions: Invalid argument (os error 22)"
454540 ) ;
455541
456542 let gm = single_region_mem_raw ( 0x1000 ) ;
457- let res = vm. register_memory_regions ( gm) ;
543+ let res = vm. register_memory_regions ( gm, None ) ;
458544 res. unwrap ( ) ;
459545 }
460546
@@ -489,7 +575,7 @@ pub(crate) mod tests {
489575
490576 let region = GuestRegionMmap :: new ( region, GuestAddress ( i as u64 * 0x1000 ) ) . unwrap ( ) ;
491577
492- let res = vm. register_memory_region ( region) ;
578+ let res = vm. register_memory_region ( region, None ) ;
493579
494580 if i >= max_nr_regions {
495581 assert ! (
0 commit comments