• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Stable C library for AVF.
16 
17 use std::ffi::CStr;
18 use std::fs::File;
19 use std::os::fd::{FromRawFd, IntoRawFd};
20 use std::os::raw::{c_char, c_int};
21 use std::ptr;
22 use std::time::Duration;
23 
24 use android_system_virtualizationservice::{
25     aidl::android::system::virtualizationservice::{
26         AssignedDevices::AssignedDevices, CpuOptions::CpuOptions,
27         CpuOptions::CpuTopology::CpuTopology, CustomMemoryBackingFile::CustomMemoryBackingFile,
28         DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
29         VirtualMachineConfig::VirtualMachineConfig,
30         VirtualMachineRawConfig::VirtualMachineRawConfig,
31     },
32     binder::{ParcelFileDescriptor, Strong},
33 };
34 use avf_bindgen::AVirtualMachineStopReason;
35 use libc::timespec;
36 use log::error;
37 use vmclient::{DeathReason, VirtualizationService, VmInstance};
38 
39 /// Create a new virtual machine config object with no properties.
40 #[no_mangle]
AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig41 pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
42     let config = Box::new(VirtualMachineRawConfig {
43         platformVersion: "~1.0".to_owned(),
44         ..Default::default()
45     });
46     Box::into_raw(config)
47 }
48 
49 /// Destroy a virtual machine config object.
50 ///
51 /// # Safety
52 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
53 /// used after deletion.
54 #[no_mangle]
AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig)55 pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
56     if !config.is_null() {
57         // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
58         // AVirtualMachineRawConfig_create. It's the only reference to the object.
59         unsafe {
60             let _ = Box::from_raw(config);
61         }
62     }
63 }
64 
65 /// Set a name of a virtual machine.
66 ///
67 /// # Safety
68 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
69 #[no_mangle]
AVirtualMachineRawConfig_setName( config: *mut VirtualMachineRawConfig, name: *const c_char, ) -> c_int70 pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
71     config: *mut VirtualMachineRawConfig,
72     name: *const c_char,
73 ) -> c_int {
74     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
75     // AVirtualMachineRawConfig_create. It's the only reference to the object.
76     let config = unsafe { &mut *config };
77     // SAFETY: `name` is assumed to be a pointer to a valid C string.
78     let name = unsafe { CStr::from_ptr(name) };
79     match name.to_str() {
80         Ok(name) => {
81             config.name = name.to_owned();
82             0
83         }
84         Err(_) => -libc::EINVAL,
85     }
86 }
87 
88 /// Set an instance ID of a virtual machine.
89 ///
90 /// # Safety
91 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
92 /// valid, non-null pointer to 64-byte data.
93 #[no_mangle]
AVirtualMachineRawConfig_setInstanceId( config: *mut VirtualMachineRawConfig, instance_id: *const u8, instance_id_size: usize, ) -> c_int94 pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
95     config: *mut VirtualMachineRawConfig,
96     instance_id: *const u8,
97     instance_id_size: usize,
98 ) -> c_int {
99     if instance_id_size != 64 {
100         return -libc::EINVAL;
101     }
102 
103     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
104     // AVirtualMachineRawConfig_create. It's the only reference to the object.
105     let config = unsafe { &mut *config };
106     // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
107     // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
108     // Both never overlap.
109     unsafe {
110         ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), instance_id_size);
111     }
112     0
113 }
114 
115 /// Set a kernel image of a virtual machine.
116 ///
117 /// # Safety
118 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
119 /// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
120 /// will be closed upon `AVirtualMachineRawConfig_delete`.
121 #[no_mangle]
AVirtualMachineRawConfig_setKernel( config: *mut VirtualMachineRawConfig, fd: c_int, )122 pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
123     config: *mut VirtualMachineRawConfig,
124     fd: c_int,
125 ) {
126     let file = get_file_from_fd(fd);
127     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
128     // AVirtualMachineRawConfig_create. It's the only reference to the object.
129     let config = unsafe { &mut *config };
130     config.kernel = file.map(ParcelFileDescriptor::new);
131 }
132 
133 /// Set an init rd of a virtual machine.
134 ///
135 /// # Safety
136 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
137 /// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
138 /// will be closed upon `AVirtualMachineRawConfig_delete`.
139 #[no_mangle]
AVirtualMachineRawConfig_setInitRd( config: *mut VirtualMachineRawConfig, fd: c_int, )140 pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
141     config: *mut VirtualMachineRawConfig,
142     fd: c_int,
143 ) {
144     let file = get_file_from_fd(fd);
145     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
146     // AVirtualMachineRawConfig_create. It's the only reference to the object.
147     let config = unsafe { &mut *config };
148     config.initrd = file.map(ParcelFileDescriptor::new);
149 }
150 
151 /// Add a disk for a virtual machine.
152 ///
153 /// # Safety
154 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
155 /// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
156 /// closed upon `AVirtualMachineRawConfig_delete`.
157 #[no_mangle]
AVirtualMachineRawConfig_addDisk( config: *mut VirtualMachineRawConfig, fd: c_int, writable: bool, ) -> c_int158 pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
159     config: *mut VirtualMachineRawConfig,
160     fd: c_int,
161     writable: bool,
162 ) -> c_int {
163     let file = get_file_from_fd(fd);
164     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
165     // AVirtualMachineRawConfig_create. It's the only reference to the object.
166     let config = unsafe { &mut *config };
167     match file {
168         // partition not supported yet
169         None => -libc::EINVAL,
170         Some(file) => {
171             config.disks.push(DiskImage {
172                 image: Some(ParcelFileDescriptor::new(file)),
173                 writable,
174                 ..Default::default()
175             });
176             0
177         }
178     }
179 }
180 
181 /// Set how much memory will be given to a virtual machine.
182 ///
183 /// # Safety
184 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
185 #[no_mangle]
AVirtualMachineRawConfig_setMemoryMiB( config: *mut VirtualMachineRawConfig, memory_mib: i32, )186 pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMiB(
187     config: *mut VirtualMachineRawConfig,
188     memory_mib: i32,
189 ) {
190     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
191     // AVirtualMachineRawConfig_create. It's the only reference to the object.
192     let config = unsafe { &mut *config };
193     config.memoryMib = memory_mib;
194 }
195 
196 /// Set how much swiotlb will be given to a virtual machine.
197 ///
198 /// # Safety
199 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
200 #[no_mangle]
AVirtualMachineRawConfig_setSwiotlbMiB( config: *mut VirtualMachineRawConfig, swiotlb_mib: i32, )201 pub unsafe extern "C" fn AVirtualMachineRawConfig_setSwiotlbMiB(
202     config: *mut VirtualMachineRawConfig,
203     swiotlb_mib: i32,
204 ) {
205     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
206     // AVirtualMachineRawConfig_create. It's the only reference to the object.
207     let config = unsafe { &mut *config };
208     config.swiotlbMib = swiotlb_mib;
209 }
210 
211 /// Set vCPU count.
212 ///
213 /// # Safety
214 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
215 #[no_mangle]
AVirtualMachineRawConfig_setVCpuCount( config: *mut VirtualMachineRawConfig, n: i32, )216 pub unsafe extern "C" fn AVirtualMachineRawConfig_setVCpuCount(
217     config: *mut VirtualMachineRawConfig,
218     n: i32,
219 ) {
220     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
221     // AVirtualMachineRawConfig_create. It's the only reference to the object.
222     let config = unsafe { &mut *config };
223     config.cpuOptions = CpuOptions { cpuTopology: CpuTopology::CpuCount(n) };
224 }
225 
226 /// Set whether a virtual machine is protected or not.
227 ///
228 /// # Safety
229 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
230 #[no_mangle]
AVirtualMachineRawConfig_setProtectedVm( config: *mut VirtualMachineRawConfig, protected_vm: bool, )231 pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
232     config: *mut VirtualMachineRawConfig,
233     protected_vm: bool,
234 ) {
235     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
236     // AVirtualMachineRawConfig_create. It's the only reference to the object.
237     let config = unsafe { &mut *config };
238     config.protectedVm = protected_vm;
239 }
240 
241 /// Set whether to use an alternate, hypervisor-specific authentication method for protected VMs.
242 ///
243 /// # Safety
244 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
245 #[no_mangle]
AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod( config: *mut VirtualMachineRawConfig, enable: bool, ) -> c_int246 pub unsafe extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
247     config: *mut VirtualMachineRawConfig,
248     enable: bool,
249 ) -> c_int {
250     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
251     // AVirtualMachineRawConfig_create. It's the only reference to the object.
252     let config = unsafe { &mut *config };
253     config.enableHypervisorSpecificAuthMethod = enable;
254     // We don't validate whether this is supported until later, when the VM is started.
255     0
256 }
257 
258 /// Use the specified fd as the backing memfd for a range of the guest physical memory.
259 ///
260 /// # Safety
261 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
262 #[no_mangle]
AVirtualMachineRawConfig_addCustomMemoryBackingFile( config: *mut VirtualMachineRawConfig, fd: c_int, range_start: u64, range_end: u64, ) -> c_int263 pub unsafe extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
264     config: *mut VirtualMachineRawConfig,
265     fd: c_int,
266     range_start: u64,
267     range_end: u64,
268 ) -> c_int {
269     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
270     // AVirtualMachineRawConfig_create. It's the only reference to the object.
271     let config = unsafe { &mut *config };
272 
273     let Some(file) = get_file_from_fd(fd) else {
274         return -libc::EINVAL;
275     };
276     let Some(size) = range_end.checked_sub(range_start) else {
277         return -libc::EINVAL;
278     };
279     config.customMemoryBackingFiles.push(CustomMemoryBackingFile {
280         file: Some(ParcelFileDescriptor::new(file)),
281         // AIDL doesn't support unsigned ints, so we've got to reinterpret the bytes into a signed
282         // int.
283         rangeStart: range_start as i64,
284         size: size as i64,
285     });
286     0
287 }
288 
289 /// Add device tree overlay blob
290 ///
291 /// # Safety
292 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
293 /// file descriptor or -1. `AVirtualMachineRawConfig_setDeviceTreeOverlay` takes ownership of `fd`
294 /// and `fd` will be closed upon `AVirtualMachineRawConfig_delete`.
295 #[no_mangle]
AVirtualMachineRawConfig_setDeviceTreeOverlay( config: *mut VirtualMachineRawConfig, fd: c_int, )296 pub unsafe extern "C" fn AVirtualMachineRawConfig_setDeviceTreeOverlay(
297     config: *mut VirtualMachineRawConfig,
298     fd: c_int,
299 ) {
300     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
301     // AVirtualMachineRawConfig_create. It's the only reference to the object.
302     let config = unsafe { &mut *config };
303 
304     match get_file_from_fd(fd) {
305         Some(file) => {
306             let fd = ParcelFileDescriptor::new(file);
307             config.devices = AssignedDevices::Dtbo(Some(fd));
308         }
309         _ => {
310             config.devices = Default::default();
311         }
312     };
313 }
314 
315 /// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
316 /// AIDL service, and connect to the child process.
317 ///
318 /// # Safety
319 /// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
320 #[no_mangle]
AVirtualizationService_create( service_ptr: *mut *mut Strong<dyn IVirtualizationService>, early: bool, ) -> c_int321 pub unsafe extern "C" fn AVirtualizationService_create(
322     service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
323     early: bool,
324 ) -> c_int {
325     let virtmgr =
326         if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
327     let virtmgr = match virtmgr {
328         Ok(virtmgr) => virtmgr,
329         Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
330     };
331     match virtmgr.connect() {
332         Ok(service) => {
333             // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
334             // pointer. `service` is the only reference here and `config` takes
335             // ownership.
336             unsafe {
337                 *service_ptr = Box::into_raw(Box::new(service));
338             }
339             0
340         }
341         Err(_) => -libc::ECONNREFUSED,
342     }
343 }
344 
345 /// Destroy a VirtualizationService object.
346 ///
347 /// # Safety
348 /// `service` must be a pointer returned by `AVirtualizationService_create` or
349 /// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
350 #[no_mangle]
AVirtualizationService_destroy( service: *mut Strong<dyn IVirtualizationService>, )351 pub unsafe extern "C" fn AVirtualizationService_destroy(
352     service: *mut Strong<dyn IVirtualizationService>,
353 ) {
354     if !service.is_null() {
355         // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
356         // `AVirtualizationService_create`. It's the only reference to the object.
357         unsafe {
358             let _ = Box::from_raw(service);
359         }
360     }
361 }
362 
363 /// Create a virtual machine with given `config`.
364 ///
365 /// # Safety
366 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
367 /// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
368 /// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
369 /// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
370 /// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
371 #[no_mangle]
AVirtualMachine_createRaw( service: *const Strong<dyn IVirtualizationService>, config: *mut VirtualMachineRawConfig, console_out_fd: c_int, console_in_fd: c_int, log_fd: c_int, vm_ptr: *mut *mut VmInstance, ) -> c_int372 pub unsafe extern "C" fn AVirtualMachine_createRaw(
373     service: *const Strong<dyn IVirtualizationService>,
374     config: *mut VirtualMachineRawConfig,
375     console_out_fd: c_int,
376     console_in_fd: c_int,
377     log_fd: c_int,
378     vm_ptr: *mut *mut VmInstance,
379 ) -> c_int {
380     // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
381     // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
382     // reference to the object.
383     let service = unsafe { &*service };
384 
385     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
386     // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
387     let config = unsafe { *Box::from_raw(config) };
388     let config = VirtualMachineConfig::RawConfig(config);
389 
390     let console_out = get_file_from_fd(console_out_fd);
391     let console_in = get_file_from_fd(console_in_fd);
392     let log = get_file_from_fd(log_fd);
393 
394     match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None) {
395         Ok(vm) => {
396             // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
397             // `vm` is the only reference here and `vm_ptr` takes ownership.
398             unsafe {
399                 *vm_ptr = Box::into_raw(Box::new(vm));
400             }
401             0
402         }
403         Err(e) => {
404             error!("AVirtualMachine_createRaw failed: {e:?}");
405             -libc::EIO
406         }
407     }
408 }
409 
410 /// Start a virtual machine.
411 ///
412 /// # Safety
413 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
414 #[no_mangle]
AVirtualMachine_start(vm: *const VmInstance) -> c_int415 pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
416     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
417     // `AVirtualMachine_createRaw`. It's the only reference to the object.
418     let vm = unsafe { &*vm };
419     match vm.start(None) {
420         Ok(_) => 0,
421         Err(e) => {
422             error!("AVirtualMachine_start failed: {e:?}");
423             -libc::EIO
424         }
425     }
426 }
427 
428 /// Stop a virtual machine.
429 ///
430 /// # Safety
431 /// `vm` must be a pointer returned by `AVirtualMachine_create`.
432 #[no_mangle]
AVirtualMachine_stop(vm: *const VmInstance) -> c_int433 pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> c_int {
434     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
435     // `AVirtualMachine_createRaw`. It's the only reference to the object.
436     let vm = unsafe { &*vm };
437     match vm.stop() {
438         Ok(_) => 0,
439         Err(e) => {
440             error!("AVirtualMachine_stop failed: {e:?}");
441             -libc::EIO
442         }
443     }
444 }
445 
446 /// Open a vsock connection to the CID of the virtual machine on the given vsock port.
447 ///
448 /// # Safety
449 /// `vm` must be a pointer returned by `AVirtualMachine_create`.
450 #[no_mangle]
AVirtualMachine_connectVsock(vm: *const VmInstance, port: u32) -> c_int451 pub unsafe extern "C" fn AVirtualMachine_connectVsock(vm: *const VmInstance, port: u32) -> c_int {
452     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
453     // `AVirtualMachine_createRaw`. It's the only reference to the object.
454     let vm = unsafe { &*vm };
455     match vm.connect_vsock(port) {
456         Ok(pfd) => pfd.into_raw_fd(),
457         Err(e) => {
458             error!("AVirtualMachine_connectVsock failed: {e:?}");
459             -libc::EIO
460         }
461     }
462 }
463 
death_reason_to_stop_reason(death_reason: DeathReason) -> AVirtualMachineStopReason464 fn death_reason_to_stop_reason(death_reason: DeathReason) -> AVirtualMachineStopReason {
465     match death_reason {
466         DeathReason::VirtualizationServiceDied => {
467             AVirtualMachineStopReason::AVIRTUAL_MACHINE_VIRTUALIZATION_SERVICE_DIED
468         }
469         DeathReason::InfrastructureError => {
470             AVirtualMachineStopReason::AVIRTUAL_MACHINE_INFRASTRUCTURE_ERROR
471         }
472         DeathReason::Killed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_KILLED,
473         DeathReason::Unknown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNKNOWN,
474         DeathReason::Shutdown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN,
475         DeathReason::StartFailed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_START_FAILED,
476         DeathReason::Reboot => AVirtualMachineStopReason::AVIRTUAL_MACHINE_REBOOT,
477         DeathReason::Crash => AVirtualMachineStopReason::AVIRTUAL_MACHINE_CRASH,
478         DeathReason::PvmFirmwarePublicKeyMismatch => {
479             AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH
480         }
481         DeathReason::PvmFirmwareInstanceImageChanged => {
482             AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
483         }
484         DeathReason::Hangup => AVirtualMachineStopReason::AVIRTUAL_MACHINE_HANGUP,
485         _ => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNRECOGNISED,
486     }
487 }
488 
489 /// Wait until a virtual machine stops or the timeout elapses.
490 ///
491 /// # Safety
492 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `timeout` must be a valid
493 /// pointer to a `struct timespec` object or null. `reason` must be a valid, non-null pointer to an
494 /// AVirtualMachineStopReason object.
495 #[no_mangle]
AVirtualMachine_waitForStop( vm: *const VmInstance, timeout: *const timespec, reason: *mut AVirtualMachineStopReason, ) -> bool496 pub unsafe extern "C" fn AVirtualMachine_waitForStop(
497     vm: *const VmInstance,
498     timeout: *const timespec,
499     reason: *mut AVirtualMachineStopReason,
500 ) -> bool {
501     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
502     // AVirtualMachine_create. It's the only reference to the object.
503     let vm = unsafe { &*vm };
504 
505     let death_reason = if timeout.is_null() {
506         vm.wait_for_death()
507     } else {
508         // SAFETY: `timeout` is assumed to be a valid pointer to a `struct timespec` object if
509         // non-null.
510         let timeout = unsafe { &*timeout };
511         let timeout = Duration::new(timeout.tv_sec as u64, timeout.tv_nsec as u32);
512         match vm.wait_for_death_with_timeout(timeout) {
513             Some(death_reason) => death_reason,
514             None => return false,
515         }
516     };
517 
518     // SAFETY: `reason` is assumed to be a valid, non-null pointer to an
519     // AVirtualMachineStopReason object.
520     unsafe { *reason = death_reason_to_stop_reason(death_reason) };
521     true
522 }
523 
524 /// Destroy a virtual machine.
525 ///
526 /// # Safety
527 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
528 /// deletion.
529 #[no_mangle]
AVirtualMachine_destroy(vm: *mut VmInstance)530 pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
531     if !vm.is_null() {
532         // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
533         // AVirtualMachine_create. It's the only reference to the object.
534         unsafe {
535             let _ = Box::from_raw(vm);
536         }
537     }
538 }
539 
get_file_from_fd(fd: i32) -> Option<File>540 fn get_file_from_fd(fd: i32) -> Option<File> {
541     if fd == -1 {
542         None
543     } else {
544         // SAFETY: transferring ownership of `fd` from the caller
545         Some(unsafe { File::from_raw_fd(fd) })
546     }
547 }
548