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;
20 use std::os::raw::{c_char, c_int};
21 use std::ptr;
22 
23 use android_system_virtualizationservice::{
24     aidl::android::system::virtualizationservice::{
25         DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
26         VirtualMachineConfig::VirtualMachineConfig,
27         VirtualMachineRawConfig::VirtualMachineRawConfig,
28     },
29     binder::{ParcelFileDescriptor, Strong},
30 };
31 use avf_bindgen::StopReason;
32 use vmclient::{DeathReason, VirtualizationService, VmInstance};
33 
34 /// Create a new virtual machine config object with no properties.
35 #[no_mangle]
AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig36 pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
37     let config = Box::new(VirtualMachineRawConfig {
38         platformVersion: "~1.0".to_owned(),
39         ..Default::default()
40     });
41     Box::into_raw(config)
42 }
43 
44 /// Destroy a virtual machine config object.
45 ///
46 /// # Safety
47 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
48 /// used after deletion.
49 #[no_mangle]
AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig)50 pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
51     if !config.is_null() {
52         // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
53         // AVirtualMachineRawConfig_create. It's the only reference to the object.
54         unsafe {
55             let _ = Box::from_raw(config);
56         }
57     }
58 }
59 
60 /// Set a name of a virtual machine.
61 ///
62 /// # Safety
63 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
64 #[no_mangle]
AVirtualMachineRawConfig_setName( config: *mut VirtualMachineRawConfig, name: *const c_char, ) -> c_int65 pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
66     config: *mut VirtualMachineRawConfig,
67     name: *const c_char,
68 ) -> c_int {
69     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
70     // AVirtualMachineRawConfig_create. It's the only reference to the object.
71     let config = unsafe { &mut *config };
72     // SAFETY: `name` is assumed to be a pointer to a valid C string.
73     config.name = unsafe { CStr::from_ptr(name) }.to_string_lossy().into_owned();
74     0
75 }
76 
77 /// Set an instance ID of a virtual machine.
78 ///
79 /// # Safety
80 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
81 /// valid, non-null pointer to 64-byte data.
82 #[no_mangle]
AVirtualMachineRawConfig_setInstanceId( config: *mut VirtualMachineRawConfig, instance_id: *const u8, ) -> c_int83 pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
84     config: *mut VirtualMachineRawConfig,
85     instance_id: *const u8,
86 ) -> c_int {
87     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
88     // AVirtualMachineRawConfig_create. It's the only reference to the object.
89     let config = unsafe { &mut *config };
90     // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
91     // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
92     // Both never overlap.
93     unsafe {
94         ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), 64);
95     }
96     0
97 }
98 
99 /// Set a kernel image of a virtual machine.
100 ///
101 /// # Safety
102 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
103 /// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
104 /// will be closed upon `AVirtualMachineRawConfig_delete`.
105 #[no_mangle]
AVirtualMachineRawConfig_setKernel( config: *mut VirtualMachineRawConfig, fd: c_int, ) -> c_int106 pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
107     config: *mut VirtualMachineRawConfig,
108     fd: c_int,
109 ) -> c_int {
110     let file = get_file_from_fd(fd);
111     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
112     // AVirtualMachineRawConfig_create. It's the only reference to the object.
113     let config = unsafe { &mut *config };
114     config.kernel = file.map(ParcelFileDescriptor::new);
115     0
116 }
117 
118 /// Set an init rd of a virtual machine.
119 ///
120 /// # Safety
121 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
122 /// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
123 /// will be closed upon `AVirtualMachineRawConfig_delete`.
124 #[no_mangle]
AVirtualMachineRawConfig_setInitRd( config: *mut VirtualMachineRawConfig, fd: c_int, ) -> c_int125 pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
126     config: *mut VirtualMachineRawConfig,
127     fd: c_int,
128 ) -> c_int {
129     let file = get_file_from_fd(fd);
130     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
131     // AVirtualMachineRawConfig_create. It's the only reference to the object.
132     let config = unsafe { &mut *config };
133     config.initrd = file.map(ParcelFileDescriptor::new);
134     0
135 }
136 
137 /// Add a disk for a virtual machine.
138 ///
139 /// # Safety
140 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
141 /// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
142 /// closed upon `AVirtualMachineRawConfig_delete`.
143 #[no_mangle]
AVirtualMachineRawConfig_addDisk( config: *mut VirtualMachineRawConfig, fd: c_int, writable: bool, ) -> c_int144 pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
145     config: *mut VirtualMachineRawConfig,
146     fd: c_int,
147     writable: bool,
148 ) -> c_int {
149     let file = get_file_from_fd(fd);
150     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
151     // AVirtualMachineRawConfig_create. It's the only reference to the object.
152     let config = unsafe { &mut *config };
153     match file {
154         // partition not supported yet
155         None => -libc::EINVAL,
156         Some(file) => {
157             config.disks.push(DiskImage {
158                 image: Some(ParcelFileDescriptor::new(file)),
159                 writable,
160                 ..Default::default()
161             });
162             0
163         }
164     }
165 }
166 
167 /// Set how much memory will be given to a virtual machine.
168 ///
169 /// # Safety
170 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
171 #[no_mangle]
AVirtualMachineRawConfig_setMemoryMib( config: *mut VirtualMachineRawConfig, memory_mib: i32, ) -> c_int172 pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMib(
173     config: *mut VirtualMachineRawConfig,
174     memory_mib: i32,
175 ) -> c_int {
176     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
177     // AVirtualMachineRawConfig_create. It's the only reference to the object.
178     let config = unsafe { &mut *config };
179     config.memoryMib = memory_mib;
180     0
181 }
182 
183 /// Set whether a virtual machine is protected or not.
184 ///
185 /// # Safety
186 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
187 #[no_mangle]
AVirtualMachineRawConfig_setProtectedVm( config: *mut VirtualMachineRawConfig, protected_vm: bool, ) -> c_int188 pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
189     config: *mut VirtualMachineRawConfig,
190     protected_vm: bool,
191 ) -> c_int {
192     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
193     // AVirtualMachineRawConfig_create. It's the only reference to the object.
194     let config = unsafe { &mut *config };
195     config.protectedVm = protected_vm;
196     0
197 }
198 
199 /// Set whether a virtual machine uses memory ballooning or not.
200 ///
201 /// # Safety
202 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
203 #[no_mangle]
AVirtualMachineRawConfig_setBalloon( config: *mut VirtualMachineRawConfig, balloon: bool, ) -> c_int204 pub unsafe extern "C" fn AVirtualMachineRawConfig_setBalloon(
205     config: *mut VirtualMachineRawConfig,
206     balloon: bool,
207 ) -> c_int {
208     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
209     // AVirtualMachineRawConfig_create. It's the only reference to the object.
210     let config = unsafe { &mut *config };
211     config.noBalloon = !balloon;
212     0
213 }
214 
215 /// NOT IMPLEMENTED.
216 ///
217 /// # Returns
218 /// It always returns `-ENOTSUP`.
219 #[no_mangle]
AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod( _config: *mut VirtualMachineRawConfig, _enable: bool, ) -> c_int220 pub extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
221     _config: *mut VirtualMachineRawConfig,
222     _enable: bool,
223 ) -> c_int {
224     -libc::ENOTSUP
225 }
226 
227 /// NOT IMPLEMENTED.
228 ///
229 /// # Returns
230 /// It always returns `-ENOTSUP`.
231 #[no_mangle]
AVirtualMachineRawConfig_addCustomMemoryBackingFile( _config: *mut VirtualMachineRawConfig, _fd: c_int, _range_start: usize, _range_end: usize, ) -> c_int232 pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
233     _config: *mut VirtualMachineRawConfig,
234     _fd: c_int,
235     _range_start: usize,
236     _range_end: usize,
237 ) -> c_int {
238     -libc::ENOTSUP
239 }
240 
241 /// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
242 /// AIDL service, and connect to the child process.
243 ///
244 /// # Safety
245 /// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
246 #[no_mangle]
AVirtualizationService_create( service_ptr: *mut *mut Strong<dyn IVirtualizationService>, early: bool, ) -> c_int247 pub unsafe extern "C" fn AVirtualizationService_create(
248     service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
249     early: bool,
250 ) -> c_int {
251     let virtmgr =
252         if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
253     let virtmgr = match virtmgr {
254         Ok(virtmgr) => virtmgr,
255         Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
256     };
257     match virtmgr.connect() {
258         Ok(service) => {
259             // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
260             // pointer. `service` is the only reference here and `config` takes
261             // ownership.
262             unsafe {
263                 *service_ptr = Box::into_raw(Box::new(service));
264             }
265             0
266         }
267         Err(_) => -libc::ECONNREFUSED,
268     }
269 }
270 
271 /// Destroy a VirtualizationService object.
272 ///
273 /// # Safety
274 /// `service` must be a pointer returned by `AVirtualizationService_create` or
275 /// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
276 #[no_mangle]
AVirtualizationService_destroy( service: *mut Strong<dyn IVirtualizationService>, )277 pub unsafe extern "C" fn AVirtualizationService_destroy(
278     service: *mut Strong<dyn IVirtualizationService>,
279 ) {
280     if !service.is_null() {
281         // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
282         // `AVirtualizationService_create`. It's the only reference to the object.
283         unsafe {
284             let _ = Box::from_raw(service);
285         }
286     }
287 }
288 
289 /// Create a virtual machine with given `config`.
290 ///
291 /// # Safety
292 /// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
293 /// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
294 /// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
295 /// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
296 /// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
297 #[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_int298 pub unsafe extern "C" fn AVirtualMachine_createRaw(
299     service: *const Strong<dyn IVirtualizationService>,
300     config: *mut VirtualMachineRawConfig,
301     console_out_fd: c_int,
302     console_in_fd: c_int,
303     log_fd: c_int,
304     vm_ptr: *mut *mut VmInstance,
305 ) -> c_int {
306     // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
307     // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
308     // reference to the object.
309     let service = unsafe { &*service };
310 
311     // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
312     // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
313     let config = unsafe { *Box::from_raw(config) };
314     let config = VirtualMachineConfig::RawConfig(config);
315 
316     let console_out = get_file_from_fd(console_out_fd);
317     let console_in = get_file_from_fd(console_in_fd);
318     let log = get_file_from_fd(log_fd);
319 
320     match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
321         Ok(vm) => {
322             // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
323             // `vm` is the only reference here and `vm_ptr` takes ownership.
324             unsafe {
325                 *vm_ptr = Box::into_raw(Box::new(vm));
326             }
327             0
328         }
329         Err(_) => -libc::EIO,
330     }
331 }
332 
333 /// Start a virtual machine.
334 ///
335 /// # Safety
336 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
337 #[no_mangle]
AVirtualMachine_start(vm: *const VmInstance) -> c_int338 pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
339     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
340     // `AVirtualMachine_createRaw`. It's the only reference to the object.
341     let vm = unsafe { &*vm };
342     match vm.start() {
343         Ok(_) => 0,
344         Err(_) => -libc::EIO,
345     }
346 }
347 
348 /// Stop a virtual machine.
349 ///
350 /// # Safety
351 /// `vm` must be a pointer returned by `AVirtualMachine_create`.
352 #[no_mangle]
AVirtualMachine_stop(vm: *const VmInstance) -> c_int353 pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> c_int {
354     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
355     // `AVirtualMachine_createRaw`. It's the only reference to the object.
356     let vm = unsafe { &*vm };
357     match vm.stop() {
358         Ok(_) => 0,
359         Err(_) => -libc::EIO,
360     }
361 }
362 
363 /// Wait until a virtual machine stops.
364 ///
365 /// # Safety
366 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
367 #[no_mangle]
AVirtualMachine_waitForStop(vm: *const VmInstance) -> StopReason368 pub unsafe extern "C" fn AVirtualMachine_waitForStop(vm: *const VmInstance) -> StopReason {
369     // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
370     // AVirtualMachine_create. It's the only reference to the object.
371     let vm = unsafe { &*vm };
372     match vm.wait_for_death() {
373         DeathReason::VirtualizationServiceDied => StopReason::VIRTUALIZATION_SERVICE_DIED,
374         DeathReason::InfrastructureError => StopReason::INFRASTRUCTURE_ERROR,
375         DeathReason::Killed => StopReason::KILLED,
376         DeathReason::Unknown => StopReason::UNKNOWN,
377         DeathReason::Shutdown => StopReason::SHUTDOWN,
378         DeathReason::StartFailed => StopReason::START_FAILED,
379         DeathReason::Reboot => StopReason::REBOOT,
380         DeathReason::Crash => StopReason::CRASH,
381         DeathReason::PvmFirmwarePublicKeyMismatch => StopReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH,
382         DeathReason::PvmFirmwareInstanceImageChanged => {
383             StopReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
384         }
385         DeathReason::Hangup => StopReason::HANGUP,
386         _ => StopReason::UNRECOGNISED,
387     }
388 }
389 
390 /// Destroy a virtual machine.
391 ///
392 /// # Safety
393 /// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
394 /// deletion.
395 #[no_mangle]
AVirtualMachine_destroy(vm: *mut VmInstance)396 pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
397     if !vm.is_null() {
398         // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
399         // AVirtualMachine_create. It's the only reference to the object.
400         unsafe {
401             let _ = Box::from_raw(vm);
402         }
403     }
404 }
405 
get_file_from_fd(fd: i32) -> Option<File>406 fn get_file_from_fd(fd: i32) -> Option<File> {
407     if fd == -1 {
408         None
409     } else {
410         // SAFETY: transferring ownership of `fd` from the caller
411         Some(unsafe { File::from_raw_fd(fd) })
412     }
413 }
414