@@ -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,78 @@ 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+ // SAFETY: the arguments to mmap cannot cause any memory unsafety in the rust sense
208+ let addr = unsafe {
209+ libc:: mmap (
210+ std:: ptr:: null_mut ( ) ,
211+ usize:: try_from ( file. metadata ( ) . unwrap ( ) . len ( ) )
212+ . expect ( "userfault bitmap file size is too large" ) ,
213+ libc:: PROT_WRITE ,
214+ libc:: MAP_SHARED ,
215+ file. as_raw_fd ( ) ,
216+ 0 ,
217+ )
218+ } ;
219+
220+ if addr == libc:: MAP_FAILED {
221+ panic ! (
222+ "Failed to mmap userfault bitmap file: {}" ,
223+ std:: io:: Error :: last_os_error( )
224+ ) ;
225+ }
226+
227+ Some ( addr as u64 )
228+ }
229+ None => None ,
230+ } ;
231+
185232 for region in regions {
186- self . register_memory_region ( region) ?
233+ self . register_memory_region ( region, addr ) ?
187234 }
188235
189236 Ok ( ( ) )
190237 }
191238
239+ // TODO: remove when userfault support is merged upstream
240+ fn set_user_memory_region2 (
241+ & self ,
242+ user_memory_region2 : kvm_userspace_memory_region2 ,
243+ ) -> Result < ( ) , VmError > {
244+ ioctl_iow_nr ! (
245+ KVM_SET_USER_MEMORY_REGION2 ,
246+ KVMIO ,
247+ 0x49 ,
248+ kvm_userspace_memory_region2
249+ ) ;
250+
251+ #[ allow( clippy:: undocumented_unsafe_blocks) ]
252+ let ret = unsafe {
253+ ioctl_with_ref (
254+ self . fd ( ) ,
255+ KVM_SET_USER_MEMORY_REGION2 ( ) ,
256+ & user_memory_region2,
257+ )
258+ } ;
259+ if ret == 0 {
260+ Ok ( ( ) )
261+ } else {
262+ Err ( VmError :: SetUserMemoryRegion ( kvm_ioctls:: Error :: last ( ) ) )
263+ }
264+ }
265+
192266 /// Register a new memory region to this [`Vm`].
193- pub fn register_memory_region ( & mut self , region : GuestRegionMmap ) -> Result < ( ) , VmError > {
267+ pub fn register_memory_region (
268+ & mut self ,
269+ region : GuestRegionMmap ,
270+ userfault_addr : Option < u64 > ,
271+ ) -> Result < ( ) , VmError > {
272+ // TODO: take it from kvm-bindings when merged upstream
273+ const KVM_MEM_USERFAULT : u32 = 1 << 3 ;
274+
194275 let next_slot = self
195276 . guest_memory ( )
196277 . num_regions ( )
@@ -218,6 +299,18 @@ impl Vm {
218299 ( 0 , 0 )
219300 } ;
220301
302+ let userfault_bitmap = match userfault_addr {
303+ Some ( addr) => {
304+ flags |= KVM_MEM_USERFAULT ;
305+
306+ let file_offset_start = region. file_offset ( ) . unwrap ( ) . start ( ) ;
307+ let pages_offset = file_offset_start / ( host_page_size ( ) as u64 ) ;
308+ let bytes_offset = pages_offset / ( u8:: BITS as u64 ) ;
309+ addr + bytes_offset
310+ }
311+ None => 0 ,
312+ } ;
313+
221314 let memory_region = kvm_userspace_memory_region2 {
222315 slot : next_slot,
223316 guest_phys_addr : region. start_addr ( ) . raw_value ( ) ,
@@ -226,18 +319,14 @@ impl Vm {
226319 flags,
227320 guest_memfd,
228321 guest_memfd_offset,
322+ userfault_bitmap,
229323 ..Default :: default ( )
230324 } ;
231325
232326 let new_guest_memory = self . common . guest_memory . insert_region ( Arc :: new ( region) ) ?;
233327
234328 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- }
329+ self . set_user_memory_region2 ( memory_region) ?;
241330 } else {
242331 // Something is seriously wrong if we manage to set these fields on a host that doesn't
243332 // even allow creation of guest_memfds!
@@ -417,7 +506,7 @@ pub(crate) mod tests {
417506 pub ( crate ) fn setup_vm_with_memory ( mem_size : usize ) -> ( Kvm , Vm ) {
418507 let ( kvm, mut vm) = setup_vm ( ) ;
419508 let gm = single_region_mem_raw ( mem_size) ;
420- vm. register_memory_regions ( gm) . unwrap ( ) ;
509+ vm. register_memory_regions ( gm, None ) . unwrap ( ) ;
421510 ( kvm, vm)
422511 }
423512
@@ -447,14 +536,14 @@ pub(crate) mod tests {
447536 // Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE
448537 // will result in error.
449538 let gm = single_region_mem_raw ( 0x10 ) ;
450- let res = vm. register_memory_regions ( gm) ;
539+ let res = vm. register_memory_regions ( gm, None ) ;
451540 assert_eq ! (
452541 res. unwrap_err( ) . to_string( ) ,
453542 "Cannot set the memory regions: Invalid argument (os error 22)"
454543 ) ;
455544
456545 let gm = single_region_mem_raw ( 0x1000 ) ;
457- let res = vm. register_memory_regions ( gm) ;
546+ let res = vm. register_memory_regions ( gm, None ) ;
458547 res. unwrap ( ) ;
459548 }
460549
@@ -489,7 +578,7 @@ pub(crate) mod tests {
489578
490579 let region = GuestRegionMmap :: new ( region, GuestAddress ( i as u64 * 0x1000 ) ) . unwrap ( ) ;
491580
492- let res = vm. register_memory_region ( region) ;
581+ let res = vm. register_memory_region ( region, None ) ;
493582
494583 if i >= max_nr_regions {
495584 assert ! (
0 commit comments