@@ -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
@@ -154,24 +198,69 @@ impl Vm {
154198 } )
155199 }
156200
201+ fn create_userfault_channels (
202+ & self ,
203+ secret_free : bool ,
204+ ) -> Result < ( Option < UserfaultChannel > , Option < UserfaultChannel > ) , std:: io:: Error > {
205+ if secret_free {
206+ let ( receiver_vcpu_to_vm, sender_vcpu_to_vm) = pipe2 ( libc:: O_NONBLOCK ) ?;
207+ let ( receiver_vm_to_vcpu, sender_vm_to_vcpu) = pipe2 ( 0 ) ?;
208+ Ok ( (
209+ Some ( UserfaultChannel {
210+ sender : sender_vcpu_to_vm,
211+ receiver : receiver_vm_to_vcpu,
212+ } ) ,
213+ Some ( UserfaultChannel {
214+ sender : sender_vm_to_vcpu,
215+ receiver : receiver_vcpu_to_vm,
216+ } ) ,
217+ ) )
218+ } else {
219+ Ok ( ( None , None ) )
220+ }
221+ }
222+
157223 /// Creates the specified number of [`Vcpu`]s.
158224 ///
159225 /// 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 > {
226+ pub fn create_vcpus (
227+ & mut self ,
228+ vcpu_count : u8 ,
229+ secret_free : bool ,
230+ ) -> Result < ( Vec < Vcpu > , EventFd , Option < Vec < UserfaultChannel > > ) , VmError > {
161231 self . arch_pre_create_vcpus ( vcpu_count) ?;
162232
163233 let exit_evt = EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( VmError :: EventFd ) ?;
164234
165235 let mut vcpus = Vec :: with_capacity ( vcpu_count as usize ) ;
236+ let mut userfault_channels = Vec :: with_capacity ( vcpu_count as usize ) ;
166237 for cpu_idx in 0 ..vcpu_count {
167238 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 ) ?;
239+
240+ let ( vcpu_channel, vmm_channel) = self
241+ . create_userfault_channels ( secret_free)
242+ . map_err ( VmError :: UserfaultChannel ) ?;
243+
244+ let vcpu =
245+ Vcpu :: new ( cpu_idx, self , exit_evt, vcpu_channel) . map_err ( VmError :: CreateVcpu ) ?;
169246 vcpus. push ( vcpu) ;
247+
248+ if secret_free {
249+ userfault_channels. push ( vmm_channel. unwrap ( ) ) ;
250+ }
170251 }
171252
172253 self . arch_post_create_vcpus ( vcpu_count) ?;
173254
174- Ok ( ( vcpus, exit_evt) )
255+ Ok ( (
256+ vcpus,
257+ exit_evt,
258+ if secret_free {
259+ Some ( userfault_channels)
260+ } else {
261+ None
262+ } ,
263+ ) )
175264 }
176265
177266 /// Create a guest_memfd of the specified size
@@ -602,7 +691,7 @@ pub(crate) mod tests {
602691 let vcpu_count = 2 ;
603692 let ( _, mut vm) = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ;
604693
605- let ( vcpu_vec, _) = vm. create_vcpus ( vcpu_count) . unwrap ( ) ;
694+ let ( vcpu_vec, _, _ ) = vm. create_vcpus ( vcpu_count, false ) . unwrap ( ) ;
606695
607696 assert_eq ! ( vcpu_vec. len( ) , vcpu_count as usize ) ;
608697 }
0 commit comments