@@ -17,8 +17,8 @@ use std::sync::{Arc, Mutex, MutexGuard};
17
17
use kvm_bindings:: KVM_IRQCHIP_IOAPIC ;
18
18
use kvm_bindings:: {
19
19
KVM_IRQ_ROUTING_IRQCHIP , KVM_IRQ_ROUTING_MSI , KVM_MEM_GUEST_MEMFD , KVM_MEM_LOG_DIRTY_PAGES ,
20
- KVM_MSI_VALID_DEVID , KvmIrqRouting , kvm_create_guest_memfd, kvm_irq_routing_entry,
21
- kvm_userspace_memory_region, kvm_userspace_memory_region2 ,
20
+ KVM_MSI_VALID_DEVID , KVMIO , KvmIrqRouting , kvm_create_guest_memfd, kvm_irq_routing_entry,
21
+ kvm_userspace_memory_region,
22
22
} ;
23
23
use kvm_ioctls:: { Cap , VmFd } ;
24
24
use log:: { debug, error} ;
@@ -29,6 +29,8 @@ use vm_device::interrupt::{
29
29
} ;
30
30
use vmm_sys_util:: errno;
31
31
use vmm_sys_util:: eventfd:: EventFd ;
32
+ use vmm_sys_util:: ioctl:: ioctl_with_ref;
33
+ use vmm_sys_util:: ioctl_iow_nr;
32
34
33
35
pub use crate :: arch:: { ArchVm as Vm , ArchVmError , VmState } ;
34
36
use crate :: arch:: { GSI_MSI_END , host_page_size} ;
@@ -289,6 +291,24 @@ pub enum VmError {
289
291
GuestMemfdNotSupported ,
290
292
}
291
293
294
+ // Upstream `kvm_userspace_memory_region2` definition does not include `userfault_bitmap` field yet.
295
+ // TODO: revert to `kvm_userspace_memory_region2` from kvm-bindings
296
+ #[ allow( non_camel_case_types) ]
297
+ #[ repr( C ) ]
298
+ #[ derive( Debug , Default , Copy , Clone , PartialEq ) ]
299
+ struct kvm_userspace_memory_region2 {
300
+ slot : u32 ,
301
+ flags : u32 ,
302
+ guest_phys_addr : u64 ,
303
+ memory_size : u64 ,
304
+ userspace_addr : u64 ,
305
+ guest_memfd_offset : u64 ,
306
+ guest_memfd : u32 ,
307
+ pad1 : u32 ,
308
+ userfault_bitmap : u64 ,
309
+ pad2 : [ u64 ; 13 ] ,
310
+ }
311
+
292
312
/// Contains Vm functions that are usable across CPU architectures
293
313
impl Vm {
294
314
/// Create a KVM VM
@@ -395,16 +415,61 @@ impl Vm {
395
415
pub fn register_memory_regions (
396
416
& mut self ,
397
417
regions : Vec < GuestRegionMmap > ,
418
+ mut userfault_bitmap : Option < & mut [ u8 ] > ,
398
419
) -> Result < ( ) , VmError > {
399
420
for region in regions {
400
- self . register_memory_region ( region) ?
421
+ let bitmap_slice = if let Some ( remaining) = userfault_bitmap {
422
+ let region_len = u64_to_usize ( region. len ( ) ) ;
423
+ // Firecracker does not allow sub-MB granularity when allocating guest memory
424
+ assert_eq ! ( region_len % ( host_page_size( ) * u8 :: BITS as usize ) , 0 ) ;
425
+ let bitmap_len = region_len / host_page_size ( ) / ( u8:: BITS as usize ) ;
426
+ let ( head, tail) = remaining. split_at_mut ( bitmap_len) ;
427
+ userfault_bitmap = Some ( tail) ;
428
+ Some ( head)
429
+ } else {
430
+ None
431
+ } ;
432
+ self . register_memory_region ( region, bitmap_slice) ?
401
433
}
402
-
403
434
Ok ( ( ) )
404
435
}
405
436
437
+ // TODO: remove when userfault support is merged upstream
438
+ fn set_user_memory_region2 (
439
+ & self ,
440
+ user_memory_region2 : kvm_userspace_memory_region2 ,
441
+ ) -> Result < ( ) , VmError > {
442
+ ioctl_iow_nr ! (
443
+ KVM_SET_USER_MEMORY_REGION2 ,
444
+ KVMIO ,
445
+ 0x49 ,
446
+ kvm_userspace_memory_region2
447
+ ) ;
448
+
449
+ #[ allow( clippy:: undocumented_unsafe_blocks) ]
450
+ let ret = unsafe {
451
+ ioctl_with_ref (
452
+ self . fd ( ) ,
453
+ KVM_SET_USER_MEMORY_REGION2 ( ) ,
454
+ & user_memory_region2,
455
+ )
456
+ } ;
457
+ if ret == 0 {
458
+ Ok ( ( ) )
459
+ } else {
460
+ Err ( VmError :: SetUserMemoryRegion ( kvm_ioctls:: Error :: last ( ) ) )
461
+ }
462
+ }
463
+
406
464
/// Register a new memory region to this [`Vm`].
407
- pub fn register_memory_region ( & mut self , region : GuestRegionMmap ) -> Result < ( ) , VmError > {
465
+ pub fn register_memory_region (
466
+ & mut self ,
467
+ region : GuestRegionMmap ,
468
+ userfault_bitmap : Option < & mut [ u8 ] > ,
469
+ ) -> Result < ( ) , VmError > {
470
+ // TODO: take it from kvm-bindings when merged upstream
471
+ const KVM_MEM_USERFAULT : u32 = 1 << 3 ;
472
+
408
473
let next_slot = self
409
474
. guest_memory ( )
410
475
. num_regions ( )
@@ -432,6 +497,14 @@ impl Vm {
432
497
( 0 , 0 )
433
498
} ;
434
499
500
+ let userfault_bitmap = match userfault_bitmap {
501
+ Some ( addr) => {
502
+ flags |= KVM_MEM_USERFAULT ;
503
+ addr. as_ptr ( ) as u64
504
+ }
505
+ None => 0 ,
506
+ } ;
507
+
435
508
let memory_region = kvm_userspace_memory_region2 {
436
509
slot : next_slot,
437
510
guest_phys_addr : region. start_addr ( ) . raw_value ( ) ,
@@ -440,24 +513,22 @@ impl Vm {
440
513
flags,
441
514
guest_memfd,
442
515
guest_memfd_offset,
516
+ userfault_bitmap,
443
517
..Default :: default ( )
444
518
} ;
445
519
446
520
let new_guest_memory = self . common . guest_memory . insert_region ( Arc :: new ( region) ) ?;
447
521
448
522
if self . fd ( ) . check_extension ( Cap :: UserMemory2 ) {
449
- // SAFETY: We are passing a valid memory region and operate on a valid KVM FD.
450
- unsafe {
451
- self . fd ( )
452
- . set_user_memory_region2 ( memory_region)
453
- . map_err ( VmError :: SetUserMemoryRegion ) ?;
454
- }
523
+ self . set_user_memory_region2 ( memory_region) ?;
455
524
} else {
456
525
// Something is seriously wrong if we manage to set these fields on a host that doesn't
457
526
// even allow creation of guest_memfds!
458
527
assert_eq ! ( memory_region. guest_memfd, 0 ) ;
459
528
assert_eq ! ( memory_region. guest_memfd_offset, 0 ) ;
529
+ assert_eq ! ( memory_region. userfault_bitmap, 0 ) ;
460
530
assert_eq ! ( memory_region. flags & KVM_MEM_GUEST_MEMFD , 0 ) ;
531
+ assert_eq ! ( memory_region. flags & KVM_MEM_USERFAULT , 0 ) ;
461
532
462
533
// SAFETY: We are passing a valid memory region and operate on a valid KVM FD.
463
534
unsafe {
@@ -789,7 +860,7 @@ pub(crate) mod tests {
789
860
pub ( crate ) fn setup_vm_with_memory ( mem_size : usize ) -> ( Kvm , Vm ) {
790
861
let ( kvm, mut vm) = setup_vm ( ) ;
791
862
let gm = single_region_mem_raw ( mem_size) ;
792
- vm. register_memory_regions ( gm) . unwrap ( ) ;
863
+ vm. register_memory_regions ( gm, None ) . unwrap ( ) ;
793
864
( kvm, vm)
794
865
}
795
866
@@ -819,14 +890,14 @@ pub(crate) mod tests {
819
890
// Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE
820
891
// will result in error.
821
892
let gm = single_region_mem_raw ( 0x10 ) ;
822
- let res = vm. register_memory_regions ( gm) ;
893
+ let res = vm. register_memory_regions ( gm, None ) ;
823
894
assert_eq ! (
824
895
res. unwrap_err( ) . to_string( ) ,
825
896
"Cannot set the memory regions: Invalid argument (os error 22)"
826
897
) ;
827
898
828
899
let gm = single_region_mem_raw ( 0x1000 ) ;
829
- let res = vm. register_memory_regions ( gm) ;
900
+ let res = vm. register_memory_regions ( gm, None ) ;
830
901
res. unwrap ( ) ;
831
902
}
832
903
@@ -861,7 +932,7 @@ pub(crate) mod tests {
861
932
862
933
let region = GuestRegionMmap :: new ( region, GuestAddress ( i as u64 * 0x1000 ) ) . unwrap ( ) ;
863
934
864
- let res = vm. register_memory_region ( region) ;
935
+ let res = vm. register_memory_region ( region, None ) ;
865
936
866
937
if max_nr_regions <= i {
867
938
assert ! (
0 commit comments