@@ -12,6 +12,7 @@ use std::os::fd::{AsFd, AsRawFd, FromRawFd};
1212use std:: path:: Path ;
1313use std:: sync:: Arc ;
1414
15+ use bincode:: { Decode , Encode } ;
1516use kvm_bindings:: {
1617 KVM_MEM_GUEST_MEMFD , KVM_MEM_LOG_DIRTY_PAGES , KVM_MEMORY_ATTRIBUTE_PRIVATE , KVMIO ,
1718 kvm_create_guest_memfd, kvm_memory_attributes, kvm_userspace_memory_region,
@@ -36,6 +37,47 @@ use crate::{DirtyBitmap, Vcpu, mem_size_mib};
3637
3738pub ( crate ) const KVM_GMEM_NO_DIRECT_MAP : u64 = 1 ;
3839
40+ /// KVM userfault information
41+ #[ derive( Copy , Clone , Decode , Default , Eq , PartialEq , Debug , Encode ) ]
42+ pub struct UserfaultData {
43+ /// Flags
44+ pub flags : u64 ,
45+ /// Guest physical address
46+ pub gpa : u64 ,
47+ /// Size
48+ pub size : u64 ,
49+ }
50+
51+ /// KVM userfault channel
52+ #[ derive( Debug ) ]
53+ pub struct UserfaultChannel {
54+ /// Sender
55+ pub sender : File ,
56+ /// Receiver
57+ pub receiver : File ,
58+ }
59+
60+ fn pipe2 ( flags : libc:: c_int ) -> std:: io:: Result < ( File , File ) > {
61+ let mut fds = [ 0 , 0 ] ;
62+
63+ // SAFETY: pipe2() is safe to call with a valid mutable pointer to an array of 2 integers
64+ // The fds array is stack-allocated and lives for the entire unsafe block.
65+ let res = unsafe { libc:: pipe2 ( fds. as_mut_ptr ( ) , flags) } ;
66+
67+ if res == 0 {
68+ Ok ( (
69+ // SAFETY: fds[0] contains a valid file descriptor for the read end of the pipe
70+ // We only convert successful pipe2() calls, and each fd is used exactly once.
71+ unsafe { File :: from_raw_fd ( fds[ 0 ] ) } ,
72+ // SAFETY: fds[1] contains a valid file descriptor for the write end of the pipe
73+ // We only convert successful pipe2() calls, and each fd is used exactly once.
74+ unsafe { File :: from_raw_fd ( fds[ 1 ] ) } ,
75+ ) )
76+ } else {
77+ Err ( std:: io:: Error :: last_os_error ( ) )
78+ }
79+ }
80+
3981/// Architecture independent parts of a VM.
4082#[ derive( Debug ) ]
4183pub struct VmCommon {
@@ -60,6 +102,8 @@ pub enum VmError {
60102 Arch ( #[ from] ArchVmError ) ,
61103 /// Error during eventfd operations: {0}
62104 EventFd ( std:: io:: Error ) ,
105+ /// Failed to create a userfault channel: {0}
106+ UserfaultChannel ( std:: io:: Error ) ,
63107 /// Failed to create vcpu: {0}
64108 CreateVcpu ( VcpuError ) ,
65109 /// The number of configured slots is bigger than the maximum reported by KVM
@@ -92,6 +136,8 @@ struct kvm_userspace_memory_region2 {
92136 pad2 : [ u64 ; 13 ] ,
93137}
94138
139+ type VcpuCreationResult = Result < ( Vec < Vcpu > , EventFd , Option < Vec < UserfaultChannel > > ) , VmError > ;
140+
95141/// Contains Vm functions that are usable across CPU architectures
96142impl Vm {
97143 /// Create a KVM VM
@@ -154,24 +200,65 @@ impl Vm {
154200 } )
155201 }
156202
203+ fn create_userfault_channels (
204+ & self ,
205+ secret_free : bool ,
206+ ) -> Result < ( Option < UserfaultChannel > , Option < UserfaultChannel > ) , std:: io:: Error > {
207+ if secret_free {
208+ let ( receiver_vcpu_to_vm, sender_vcpu_to_vm) = pipe2 ( libc:: O_NONBLOCK ) ?;
209+ let ( receiver_vm_to_vcpu, sender_vm_to_vcpu) = pipe2 ( 0 ) ?;
210+ Ok ( (
211+ Some ( UserfaultChannel {
212+ sender : sender_vcpu_to_vm,
213+ receiver : receiver_vm_to_vcpu,
214+ } ) ,
215+ Some ( UserfaultChannel {
216+ sender : sender_vm_to_vcpu,
217+ receiver : receiver_vcpu_to_vm,
218+ } ) ,
219+ ) )
220+ } else {
221+ Ok ( ( None , None ) )
222+ }
223+ }
224+
157225 /// Creates the specified number of [`Vcpu`]s.
158226 ///
159227 /// The returned [`EventFd`] is written to whenever any of the vcpus exit.
160- pub fn create_vcpus ( & mut self , vcpu_count : u8 ) -> Result < ( Vec < Vcpu > , EventFd ) , VmError > {
228+ pub fn create_vcpus ( & mut self , vcpu_count : u8 , secret_free : bool ) -> VcpuCreationResult {
161229 self . arch_pre_create_vcpus ( vcpu_count) ?;
162230
163231 let exit_evt = EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( VmError :: EventFd ) ?;
164232
165233 let mut vcpus = Vec :: with_capacity ( vcpu_count as usize ) ;
234+ let mut userfault_channels = Vec :: with_capacity ( vcpu_count as usize ) ;
166235 for cpu_idx in 0 ..vcpu_count {
167236 let exit_evt = exit_evt. try_clone ( ) . map_err ( VmError :: EventFd ) ?;
168- let vcpu = Vcpu :: new ( cpu_idx, self , exit_evt) . map_err ( VmError :: CreateVcpu ) ?;
237+
238+ let ( vcpu_channel, vmm_channel) = self
239+ . create_userfault_channels ( secret_free)
240+ . map_err ( VmError :: UserfaultChannel ) ?;
241+
242+ let vcpu =
243+ Vcpu :: new ( cpu_idx, self , exit_evt, vcpu_channel) . map_err ( VmError :: CreateVcpu ) ?;
169244 vcpus. push ( vcpu) ;
245+
246+ if secret_free {
247+ userfault_channels. push ( vmm_channel. unwrap ( ) ) ;
248+ }
170249 }
171250
172251 self . arch_post_create_vcpus ( vcpu_count) ?;
173252
174- Ok ( ( vcpus, exit_evt) )
253+ Ok ( (
254+ vcpus,
255+ exit_evt,
256+ if secret_free {
257+ Some ( userfault_channels)
258+ } else {
259+ None
260+ } ,
261+ ) )
175262 }
176263
177264 /// Create a guest_memfd of the specified size
@@ -605,7 +692,7 @@ pub(crate) mod tests {
605692 let vcpu_count = 2 ;
606693 let ( _, mut vm) = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ;
607694
608- let ( vcpu_vec, _) = vm. create_vcpus ( vcpu_count) . unwrap ( ) ;
695+ let ( vcpu_vec, _, _ ) = vm. create_vcpus ( vcpu_count, false ) . unwrap ( ) ;
609696
610697 assert_eq ! ( vcpu_vec. len( ) , vcpu_count as usize ) ;
611698 }
0 commit comments