blob: 3fa1b754d97cb2a7d841e8d2f802ac61b220bf75 [file] [log] [blame]
Inseob Kimf3536de2024-11-22 14:00:57 +09001// 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
17use std::ffi::CStr;
18use std::fs::File;
Inseob Kim32e299c2024-12-09 15:27:40 +090019use std::os::fd::{FromRawFd, IntoRawFd};
Inseob Kimf3536de2024-11-22 14:00:57 +090020use std::os::raw::{c_char, c_int};
21use std::ptr;
Inseob Kim06064252024-12-05 17:52:25 +090022use std::time::Duration;
Inseob Kimf3536de2024-11-22 14:00:57 +090023
24use android_system_virtualizationservice::{
25 aidl::android::system::virtualizationservice::{
Elie Kheirallahb4b2f242025-01-23 03:38:07 +000026 AssignedDevices::AssignedDevices, CpuOptions::CpuOptions,
27 CpuOptions::CpuTopology::CpuTopology, DiskImage::DiskImage,
Frederick Maylecc0eb2b2024-12-11 19:38:16 -080028 IVirtualizationService::IVirtualizationService, VirtualMachineConfig::VirtualMachineConfig,
Inseob Kimf3536de2024-11-22 14:00:57 +090029 VirtualMachineRawConfig::VirtualMachineRawConfig,
30 },
31 binder::{ParcelFileDescriptor, Strong},
32};
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -080033use avf_bindgen::AVirtualMachineStopReason;
Inseob Kim06064252024-12-05 17:52:25 +090034use libc::timespec;
Inseob Kim5faf0cb2024-12-16 20:10:11 +090035use log::error;
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -080036use vmclient::{DeathReason, VirtualizationService, VmInstance};
Inseob Kimf3536de2024-11-22 14:00:57 +090037
38/// Create a new virtual machine config object with no properties.
39#[no_mangle]
40pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
41 let config = Box::new(VirtualMachineRawConfig {
42 platformVersion: "~1.0".to_owned(),
43 ..Default::default()
44 });
45 Box::into_raw(config)
46}
47
48/// Destroy a virtual machine config object.
49///
50/// # Safety
51/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
52/// used after deletion.
53#[no_mangle]
54pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
55 if !config.is_null() {
56 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
57 // AVirtualMachineRawConfig_create. It's the only reference to the object.
58 unsafe {
59 let _ = Box::from_raw(config);
60 }
61 }
62}
63
64/// Set a name of a virtual machine.
65///
66/// # Safety
67/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
68#[no_mangle]
69pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
70 config: *mut VirtualMachineRawConfig,
71 name: *const c_char,
72) -> c_int {
73 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
74 // AVirtualMachineRawConfig_create. It's the only reference to the object.
75 let config = unsafe { &mut *config };
76 // SAFETY: `name` is assumed to be a pointer to a valid C string.
Inseob Kim06064252024-12-05 17:52:25 +090077 let name = unsafe { CStr::from_ptr(name) };
78 match name.to_str() {
79 Ok(name) => {
80 config.name = name.to_owned();
81 0
82 }
83 Err(_) => -libc::EINVAL,
84 }
Inseob Kimf3536de2024-11-22 14:00:57 +090085}
86
87/// Set an instance ID of a virtual machine.
88///
89/// # Safety
90/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
91/// valid, non-null pointer to 64-byte data.
92#[no_mangle]
93pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
94 config: *mut VirtualMachineRawConfig,
95 instance_id: *const u8,
Inseob Kim06064252024-12-05 17:52:25 +090096 instance_id_size: usize,
Inseob Kimf3536de2024-11-22 14:00:57 +090097) -> c_int {
Inseob Kim06064252024-12-05 17:52:25 +090098 if instance_id_size != 64 {
99 return -libc::EINVAL;
100 }
101
Inseob Kimf3536de2024-11-22 14:00:57 +0900102 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
103 // AVirtualMachineRawConfig_create. It's the only reference to the object.
104 let config = unsafe { &mut *config };
105 // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
106 // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
107 // Both never overlap.
108 unsafe {
Inseob Kim06064252024-12-05 17:52:25 +0900109 ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), instance_id_size);
Inseob Kimf3536de2024-11-22 14:00:57 +0900110 }
111 0
112}
113
114/// Set a kernel image of a virtual machine.
115///
116/// # Safety
117/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
118/// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
119/// will be closed upon `AVirtualMachineRawConfig_delete`.
120#[no_mangle]
121pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
122 config: *mut VirtualMachineRawConfig,
123 fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900124) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900125 let file = get_file_from_fd(fd);
126 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
127 // AVirtualMachineRawConfig_create. It's the only reference to the object.
128 let config = unsafe { &mut *config };
129 config.kernel = file.map(ParcelFileDescriptor::new);
Inseob Kimf3536de2024-11-22 14:00:57 +0900130}
131
132/// Set an init rd of a virtual machine.
133///
134/// # Safety
135/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
136/// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
137/// will be closed upon `AVirtualMachineRawConfig_delete`.
138#[no_mangle]
139pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
140 config: *mut VirtualMachineRawConfig,
141 fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900142) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900143 let file = get_file_from_fd(fd);
144 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
145 // AVirtualMachineRawConfig_create. It's the only reference to the object.
146 let config = unsafe { &mut *config };
147 config.initrd = file.map(ParcelFileDescriptor::new);
Inseob Kimf3536de2024-11-22 14:00:57 +0900148}
149
150/// Add a disk for a virtual machine.
151///
152/// # Safety
153/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
154/// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
155/// closed upon `AVirtualMachineRawConfig_delete`.
156#[no_mangle]
157pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
158 config: *mut VirtualMachineRawConfig,
159 fd: c_int,
160 writable: bool,
161) -> c_int {
162 let file = get_file_from_fd(fd);
163 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
164 // AVirtualMachineRawConfig_create. It's the only reference to the object.
165 let config = unsafe { &mut *config };
166 match file {
167 // partition not supported yet
168 None => -libc::EINVAL,
169 Some(file) => {
170 config.disks.push(DiskImage {
171 image: Some(ParcelFileDescriptor::new(file)),
172 writable,
173 ..Default::default()
174 });
175 0
176 }
177 }
178}
179
180/// Set how much memory will be given to a virtual machine.
181///
182/// # Safety
183/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
184#[no_mangle]
Frederick Mayleb4030cf2024-12-13 15:05:54 -0800185pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMiB(
Inseob Kimf3536de2024-11-22 14:00:57 +0900186 config: *mut VirtualMachineRawConfig,
187 memory_mib: i32,
Inseob Kim06064252024-12-05 17:52:25 +0900188) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900189 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
190 // AVirtualMachineRawConfig_create. It's the only reference to the object.
191 let config = unsafe { &mut *config };
192 config.memoryMib = memory_mib;
Inseob Kimf3536de2024-11-22 14:00:57 +0900193}
194
Frederick Mayle384f5b52024-12-06 16:23:23 -0800195/// Set how much swiotlb will be given to a virtual machine.
196///
197/// # Safety
198/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
199#[no_mangle]
200pub unsafe extern "C" fn AVirtualMachineRawConfig_setSwiotlbMiB(
201 config: *mut VirtualMachineRawConfig,
202 swiotlb_mib: i32,
203) {
204 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
205 // AVirtualMachineRawConfig_create. It's the only reference to the object.
206 let config = unsafe { &mut *config };
207 config.swiotlbMib = swiotlb_mib;
208}
209
Frederick Maylecc0eb2b2024-12-11 19:38:16 -0800210/// Set vCPU count.
211///
212/// # Safety
213/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
214#[no_mangle]
215pub unsafe extern "C" fn AVirtualMachineRawConfig_setVCpuCount(
216 config: *mut VirtualMachineRawConfig,
217 n: i32,
218) {
219 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
220 // AVirtualMachineRawConfig_create. It's the only reference to the object.
221 let config = unsafe { &mut *config };
Elie Kheirallahb4b2f242025-01-23 03:38:07 +0000222 config.cpuOptions = CpuOptions { cpuTopology: CpuTopology::CpuCount(n) };
Frederick Maylecc0eb2b2024-12-11 19:38:16 -0800223}
224
Inseob Kimf3536de2024-11-22 14:00:57 +0900225/// Set whether a virtual machine is protected or not.
226///
227/// # Safety
228/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
229#[no_mangle]
230pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
231 config: *mut VirtualMachineRawConfig,
232 protected_vm: bool,
Inseob Kim06064252024-12-05 17:52:25 +0900233) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900234 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
235 // AVirtualMachineRawConfig_create. It's the only reference to the object.
236 let config = unsafe { &mut *config };
237 config.protectedVm = protected_vm;
Inseob Kimf3536de2024-11-22 14:00:57 +0900238}
239
Frederick Mayle196ca2b2024-12-18 15:27:43 -0800240/// Set whether to use an alternate, hypervisor-specific authentication method for protected VMs.
Inseob Kimf3536de2024-11-22 14:00:57 +0900241///
Frederick Mayle196ca2b2024-12-18 15:27:43 -0800242/// # Safety
243/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
Inseob Kimf3536de2024-11-22 14:00:57 +0900244#[no_mangle]
Frederick Mayle196ca2b2024-12-18 15:27:43 -0800245pub unsafe extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
246 config: *mut VirtualMachineRawConfig,
247 enable: bool,
Inseob Kimf3536de2024-11-22 14:00:57 +0900248) -> c_int {
Frederick Mayle196ca2b2024-12-18 15:27:43 -0800249 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
250 // AVirtualMachineRawConfig_create. It's the only reference to the object.
251 let config = unsafe { &mut *config };
252 config.enableHypervisorSpecificAuthMethod = enable;
253 // We don't validate whether this is supported until later, when the VM is started.
254 0
Inseob Kimf3536de2024-11-22 14:00:57 +0900255}
256
257/// NOT IMPLEMENTED.
258///
259/// # Returns
260/// It always returns `-ENOTSUP`.
261#[no_mangle]
262pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
263 _config: *mut VirtualMachineRawConfig,
264 _fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900265 _range_start: u64,
266 _range_end: u64,
Inseob Kimf3536de2024-11-22 14:00:57 +0900267) -> c_int {
268 -libc::ENOTSUP
269}
270
Jaewan Kimffc007c2024-12-12 22:15:35 +0900271/// Add device tree overlay blob
272///
273/// # Safety
274/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
275/// file descriptor or -1. `AVirtualMachineRawConfig_setDeviceTreeOverlay` takes ownership of `fd`
276/// and `fd` will be closed upon `AVirtualMachineRawConfig_delete`.
277#[no_mangle]
278pub unsafe extern "C" fn AVirtualMachineRawConfig_setDeviceTreeOverlay(
279 config: *mut VirtualMachineRawConfig,
280 fd: c_int,
281) {
282 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
283 // AVirtualMachineRawConfig_create. It's the only reference to the object.
284 let config = unsafe { &mut *config };
285
286 match get_file_from_fd(fd) {
287 Some(file) => {
288 let fd = ParcelFileDescriptor::new(file);
289 config.devices = AssignedDevices::Dtbo(Some(fd));
290 }
291 _ => {
292 config.devices = Default::default();
293 }
294 };
295}
296
Inseob Kimf3536de2024-11-22 14:00:57 +0900297/// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
298/// AIDL service, and connect to the child process.
299///
300/// # Safety
301/// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
302#[no_mangle]
303pub unsafe extern "C" fn AVirtualizationService_create(
304 service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
305 early: bool,
306) -> c_int {
307 let virtmgr =
308 if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
309 let virtmgr = match virtmgr {
310 Ok(virtmgr) => virtmgr,
311 Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
312 };
313 match virtmgr.connect() {
314 Ok(service) => {
315 // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
316 // pointer. `service` is the only reference here and `config` takes
317 // ownership.
318 unsafe {
319 *service_ptr = Box::into_raw(Box::new(service));
320 }
321 0
322 }
323 Err(_) => -libc::ECONNREFUSED,
324 }
325}
326
327/// Destroy a VirtualizationService object.
328///
329/// # Safety
330/// `service` must be a pointer returned by `AVirtualizationService_create` or
331/// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
332#[no_mangle]
333pub unsafe extern "C" fn AVirtualizationService_destroy(
334 service: *mut Strong<dyn IVirtualizationService>,
335) {
336 if !service.is_null() {
337 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
338 // `AVirtualizationService_create`. It's the only reference to the object.
339 unsafe {
340 let _ = Box::from_raw(service);
341 }
342 }
343}
344
345/// Create a virtual machine with given `config`.
346///
347/// # Safety
348/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
349/// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
350/// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
351/// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
352/// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
353#[no_mangle]
354pub unsafe extern "C" fn AVirtualMachine_createRaw(
355 service: *const Strong<dyn IVirtualizationService>,
356 config: *mut VirtualMachineRawConfig,
357 console_out_fd: c_int,
358 console_in_fd: c_int,
359 log_fd: c_int,
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800360 vm_ptr: *mut *mut VmInstance,
Inseob Kimf3536de2024-11-22 14:00:57 +0900361) -> c_int {
362 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
363 // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
364 // reference to the object.
365 let service = unsafe { &*service };
366
367 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
368 // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
369 let config = unsafe { *Box::from_raw(config) };
370 let config = VirtualMachineConfig::RawConfig(config);
371
372 let console_out = get_file_from_fd(console_out_fd);
373 let console_in = get_file_from_fd(console_in_fd);
374 let log = get_file_from_fd(log_fd);
375
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800376 match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900377 Ok(vm) => {
378 // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800379 // `vm` is the only reference here and `vm_ptr` takes ownership.
Inseob Kimf3536de2024-11-22 14:00:57 +0900380 unsafe {
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800381 *vm_ptr = Box::into_raw(Box::new(vm));
Inseob Kimf3536de2024-11-22 14:00:57 +0900382 }
383 0
384 }
Inseob Kim5faf0cb2024-12-16 20:10:11 +0900385 Err(e) => {
386 error!("AVirtualMachine_createRaw failed: {e:?}");
387 -libc::EIO
388 }
Inseob Kimf3536de2024-11-22 14:00:57 +0900389 }
390}
391
392/// Start a virtual machine.
393///
394/// # Safety
395/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
396#[no_mangle]
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800397pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
Inseob Kimf3536de2024-11-22 14:00:57 +0900398 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
399 // `AVirtualMachine_createRaw`. It's the only reference to the object.
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800400 let vm = unsafe { &*vm };
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800401 match vm.start() {
Inseob Kimf3536de2024-11-22 14:00:57 +0900402 Ok(_) => 0,
Inseob Kim5faf0cb2024-12-16 20:10:11 +0900403 Err(e) => {
404 error!("AVirtualMachine_start failed: {e:?}");
405 -libc::EIO
406 }
Inseob Kimf3536de2024-11-22 14:00:57 +0900407 }
408}
409
410/// Stop a virtual machine.
411///
412/// # Safety
413/// `vm` must be a pointer returned by `AVirtualMachine_create`.
414#[no_mangle]
415pub unsafe extern "C" fn AVirtualMachine_stop(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.stop() {
420 Ok(_) => 0,
Inseob Kim5faf0cb2024-12-16 20:10:11 +0900421 Err(e) => {
422 error!("AVirtualMachine_stop failed: {e:?}");
423 -libc::EIO
424 }
Inseob Kimf3536de2024-11-22 14:00:57 +0900425 }
426}
427
Inseob Kim32e299c2024-12-09 15:27:40 +0900428/// Open a vsock connection to the CID of the virtual machine on the given vsock port.
429///
430/// # Safety
431/// `vm` must be a pointer returned by `AVirtualMachine_create`.
432#[no_mangle]
433pub unsafe extern "C" fn AVirtualMachine_connectVsock(vm: *const VmInstance, port: u32) -> 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.connect_vsock(port) {
438 Ok(pfd) => pfd.into_raw_fd(),
Inseob Kim5faf0cb2024-12-16 20:10:11 +0900439 Err(e) => {
440 error!("AVirtualMachine_connectVsock failed: {e:?}");
441 -libc::EIO
442 }
Inseob Kim32e299c2024-12-09 15:27:40 +0900443 }
444}
445
Inseob Kim06064252024-12-05 17:52:25 +0900446fn death_reason_to_stop_reason(death_reason: DeathReason) -> AVirtualMachineStopReason {
447 match death_reason {
448 DeathReason::VirtualizationServiceDied => {
449 AVirtualMachineStopReason::AVIRTUAL_MACHINE_VIRTUALIZATION_SERVICE_DIED
450 }
451 DeathReason::InfrastructureError => {
452 AVirtualMachineStopReason::AVIRTUAL_MACHINE_INFRASTRUCTURE_ERROR
453 }
454 DeathReason::Killed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_KILLED,
455 DeathReason::Unknown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNKNOWN,
456 DeathReason::Shutdown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN,
457 DeathReason::StartFailed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_START_FAILED,
458 DeathReason::Reboot => AVirtualMachineStopReason::AVIRTUAL_MACHINE_REBOOT,
459 DeathReason::Crash => AVirtualMachineStopReason::AVIRTUAL_MACHINE_CRASH,
460 DeathReason::PvmFirmwarePublicKeyMismatch => {
461 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH
462 }
463 DeathReason::PvmFirmwareInstanceImageChanged => {
464 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
465 }
466 DeathReason::Hangup => AVirtualMachineStopReason::AVIRTUAL_MACHINE_HANGUP,
467 _ => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNRECOGNISED,
468 }
469}
470
471/// Wait until a virtual machine stops or the timeout elapses.
Inseob Kimf3536de2024-11-22 14:00:57 +0900472///
473/// # Safety
Inseob Kim06064252024-12-05 17:52:25 +0900474/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `timeout` must be a valid
475/// pointer to a `struct timespec` object or null. `reason` must be a valid, non-null pointer to an
476/// AVirtualMachineStopReason object.
Inseob Kimf3536de2024-11-22 14:00:57 +0900477#[no_mangle]
Inseob Kim06064252024-12-05 17:52:25 +0900478pub unsafe extern "C" fn AVirtualMachine_waitForStop(
479 vm: *const VmInstance,
480 timeout: *const timespec,
481 reason: *mut AVirtualMachineStopReason,
482) -> bool {
Inseob Kimf3536de2024-11-22 14:00:57 +0900483 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
484 // AVirtualMachine_create. It's the only reference to the object.
485 let vm = unsafe { &*vm };
Inseob Kim06064252024-12-05 17:52:25 +0900486
487 let death_reason = if timeout.is_null() {
488 vm.wait_for_death()
489 } else {
490 // SAFETY: `timeout` is assumed to be a valid pointer to a `struct timespec` object if
491 // non-null.
492 let timeout = unsafe { &*timeout };
493 let timeout = Duration::new(timeout.tv_sec as u64, timeout.tv_nsec as u32);
494 match vm.wait_for_death_with_timeout(timeout) {
495 Some(death_reason) => death_reason,
496 None => return false,
Inseob Kimf3536de2024-11-22 14:00:57 +0900497 }
Inseob Kim06064252024-12-05 17:52:25 +0900498 };
499
500 // SAFETY: `reason` is assumed to be a valid, non-null pointer to an
501 // AVirtualMachineStopReason object.
502 unsafe { *reason = death_reason_to_stop_reason(death_reason) };
503 true
Inseob Kimf3536de2024-11-22 14:00:57 +0900504}
505
506/// Destroy a virtual machine.
507///
508/// # Safety
509/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
510/// deletion.
511#[no_mangle]
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800512pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900513 if !vm.is_null() {
514 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
515 // AVirtualMachine_create. It's the only reference to the object.
516 unsafe {
Chaitanya Cheemala (xWF)5159c622025-01-21 08:57:09 -0800517 let _ = Box::from_raw(vm);
Inseob Kimf3536de2024-11-22 14:00:57 +0900518 }
519 }
520}
521
522fn get_file_from_fd(fd: i32) -> Option<File> {
523 if fd == -1 {
524 None
525 } else {
526 // SAFETY: transferring ownership of `fd` from the caller
527 Some(unsafe { File::from_raw_fd(fd) })
528 }
529}