@@ -13,12 +13,13 @@ use std::path::Path;
13
13
use std:: sync:: Arc ;
14
14
15
15
use 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 ,
17
17
kvm_create_guest_memfd, kvm_memory_attributes, kvm_userspace_memory_region,
18
- kvm_userspace_memory_region2,
19
18
} ;
20
19
use kvm_ioctls:: { Cap , VmFd } ;
21
20
use 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} ;
22
23
23
24
pub use crate :: arch:: { ArchVm as Vm , ArchVmError , VmState } ;
24
25
use crate :: arch:: { VM_TYPE_FOR_SECRET_FREEDOM , host_page_size} ;
@@ -73,6 +74,24 @@ pub enum VmError {
73
74
SetMemoryAttributes ( kvm_ioctls:: Error ) ,
74
75
}
75
76
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
+
76
95
/// Contains Vm functions that are usable across CPU architectures
77
96
impl Vm {
78
97
/// Create a KVM VM
@@ -181,16 +200,61 @@ impl Vm {
181
200
pub fn register_memory_regions (
182
201
& mut self ,
183
202
regions : Vec < GuestRegionMmap > ,
203
+ mut userfault_bitmap : Option < & mut [ u8 ] > ,
184
204
) -> Result < ( ) , VmError > {
185
205
for region in regions {
186
- self . register_memory_region ( region) ?
206
+ let bitmap_slice = if let Some ( remaining) = userfault_bitmap {
207
+ let region_len = u64_to_usize ( region. len ( ) ) ;
208
+ // Firecracker does not allow sub-MB granularity when allocating guest memory
209
+ assert_eq ! ( region_len % ( host_page_size( ) * u8 :: BITS as usize ) , 0 ) ;
210
+ let bitmap_len = region_len / host_page_size ( ) / ( u8:: BITS as usize ) ;
211
+ let ( head, tail) = remaining. split_at_mut ( bitmap_len) ;
212
+ userfault_bitmap = Some ( tail) ;
213
+ Some ( head)
214
+ } else {
215
+ None
216
+ } ;
217
+ self . register_memory_region ( region, bitmap_slice) ?
187
218
}
188
-
189
219
Ok ( ( ) )
190
220
}
191
221
222
+ // TODO: remove when userfault support is merged upstream
223
+ fn set_user_memory_region2 (
224
+ & self ,
225
+ user_memory_region2 : kvm_userspace_memory_region2 ,
226
+ ) -> Result < ( ) , VmError > {
227
+ ioctl_iow_nr ! (
228
+ KVM_SET_USER_MEMORY_REGION2 ,
229
+ KVMIO ,
230
+ 0x49 ,
231
+ kvm_userspace_memory_region2
232
+ ) ;
233
+
234
+ #[ allow( clippy:: undocumented_unsafe_blocks) ]
235
+ let ret = unsafe {
236
+ ioctl_with_ref (
237
+ self . fd ( ) ,
238
+ KVM_SET_USER_MEMORY_REGION2 ( ) ,
239
+ & user_memory_region2,
240
+ )
241
+ } ;
242
+ if ret == 0 {
243
+ Ok ( ( ) )
244
+ } else {
245
+ Err ( VmError :: SetUserMemoryRegion ( kvm_ioctls:: Error :: last ( ) ) )
246
+ }
247
+ }
248
+
192
249
/// Register a new memory region to this [`Vm`].
193
- pub fn register_memory_region ( & mut self , region : GuestRegionMmap ) -> Result < ( ) , VmError > {
250
+ pub fn register_memory_region (
251
+ & mut self ,
252
+ region : GuestRegionMmap ,
253
+ userfault_bitmap : Option < & mut [ u8 ] > ,
254
+ ) -> Result < ( ) , VmError > {
255
+ // TODO: take it from kvm-bindings when merged upstream
256
+ const KVM_MEM_USERFAULT : u32 = 1 << 3 ;
257
+
194
258
let next_slot = self
195
259
. guest_memory ( )
196
260
. num_regions ( )
@@ -218,6 +282,14 @@ impl Vm {
218
282
( 0 , 0 )
219
283
} ;
220
284
285
+ let userfault_bitmap = match userfault_bitmap {
286
+ Some ( addr) => {
287
+ flags |= KVM_MEM_USERFAULT ;
288
+ addr. as_ptr ( ) as u64
289
+ }
290
+ None => 0 ,
291
+ } ;
292
+
221
293
let memory_region = kvm_userspace_memory_region2 {
222
294
slot : next_slot,
223
295
guest_phys_addr : region. start_addr ( ) . raw_value ( ) ,
@@ -226,24 +298,22 @@ impl Vm {
226
298
flags,
227
299
guest_memfd,
228
300
guest_memfd_offset,
301
+ userfault_bitmap,
229
302
..Default :: default ( )
230
303
} ;
231
304
232
305
let new_guest_memory = self . common . guest_memory . insert_region ( Arc :: new ( region) ) ?;
233
306
234
307
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
- }
308
+ self . set_user_memory_region2 ( memory_region) ?;
241
309
} else {
242
310
// Something is seriously wrong if we manage to set these fields on a host that doesn't
243
311
// even allow creation of guest_memfds!
244
312
assert_eq ! ( memory_region. guest_memfd, 0 ) ;
245
313
assert_eq ! ( memory_region. guest_memfd_offset, 0 ) ;
314
+ assert_eq ! ( memory_region. userfault_bitmap, 0 ) ;
246
315
assert_eq ! ( memory_region. flags & KVM_MEM_GUEST_MEMFD , 0 ) ;
316
+ assert_eq ! ( memory_region. flags & KVM_MEM_USERFAULT , 0 ) ;
247
317
248
318
// SAFETY: We are passing a valid memory region and operate on a valid KVM FD.
249
319
unsafe {
@@ -417,7 +487,7 @@ pub(crate) mod tests {
417
487
pub ( crate ) fn setup_vm_with_memory ( mem_size : usize ) -> ( Kvm , Vm ) {
418
488
let ( kvm, mut vm) = setup_vm ( ) ;
419
489
let gm = single_region_mem_raw ( mem_size) ;
420
- vm. register_memory_regions ( gm) . unwrap ( ) ;
490
+ vm. register_memory_regions ( gm, None ) . unwrap ( ) ;
421
491
( kvm, vm)
422
492
}
423
493
@@ -447,14 +517,14 @@ pub(crate) mod tests {
447
517
// Trying to set a memory region with a size that is not a multiple of GUEST_PAGE_SIZE
448
518
// will result in error.
449
519
let gm = single_region_mem_raw ( 0x10 ) ;
450
- let res = vm. register_memory_regions ( gm) ;
520
+ let res = vm. register_memory_regions ( gm, None ) ;
451
521
assert_eq ! (
452
522
res. unwrap_err( ) . to_string( ) ,
453
523
"Cannot set the memory regions: Invalid argument (os error 22)"
454
524
) ;
455
525
456
526
let gm = single_region_mem_raw ( 0x1000 ) ;
457
- let res = vm. register_memory_regions ( gm) ;
527
+ let res = vm. register_memory_regions ( gm, None ) ;
458
528
res. unwrap ( ) ;
459
529
}
460
530
@@ -489,7 +559,7 @@ pub(crate) mod tests {
489
559
490
560
let region = GuestRegionMmap :: new ( region, GuestAddress ( i as u64 * 0x1000 ) ) . unwrap ( ) ;
491
561
492
- let res = vm. register_memory_region ( region) ;
562
+ let res = vm. register_memory_region ( region, None ) ;
493
563
494
564
if i >= max_nr_regions {
495
565
assert ! (
0 commit comments