blob: af891e8a54b4d1753b43e28c82b2556c42fa43be [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;
19use std::os::fd::FromRawFd;
20use 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::{
Frederick Maylecc0eb2b2024-12-11 19:38:16 -080026 CpuTopology::CpuTopology, DiskImage::DiskImage,
27 IVirtualizationService::IVirtualizationService, VirtualMachineConfig::VirtualMachineConfig,
Inseob Kimf3536de2024-11-22 14:00:57 +090028 VirtualMachineRawConfig::VirtualMachineRawConfig,
29 },
30 binder::{ParcelFileDescriptor, Strong},
31};
Inseob Kim06064252024-12-05 17:52:25 +090032use avf_bindgen::AVirtualMachineStopReason;
33use libc::timespec;
Inseob Kimf3536de2024-11-22 14:00:57 +090034use vmclient::{DeathReason, VirtualizationService, VmInstance};
35
36/// Create a new virtual machine config object with no properties.
37#[no_mangle]
38pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
39 let config = Box::new(VirtualMachineRawConfig {
40 platformVersion: "~1.0".to_owned(),
41 ..Default::default()
42 });
43 Box::into_raw(config)
44}
45
46/// Destroy a virtual machine config object.
47///
48/// # Safety
49/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
50/// used after deletion.
51#[no_mangle]
52pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
53 if !config.is_null() {
54 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
55 // AVirtualMachineRawConfig_create. It's the only reference to the object.
56 unsafe {
57 let _ = Box::from_raw(config);
58 }
59 }
60}
61
62/// Set a name of a virtual machine.
63///
64/// # Safety
65/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
66#[no_mangle]
67pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
68 config: *mut VirtualMachineRawConfig,
69 name: *const c_char,
70) -> c_int {
71 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
72 // AVirtualMachineRawConfig_create. It's the only reference to the object.
73 let config = unsafe { &mut *config };
74 // SAFETY: `name` is assumed to be a pointer to a valid C string.
Inseob Kim06064252024-12-05 17:52:25 +090075 let name = unsafe { CStr::from_ptr(name) };
76 match name.to_str() {
77 Ok(name) => {
78 config.name = name.to_owned();
79 0
80 }
81 Err(_) => -libc::EINVAL,
82 }
Inseob Kimf3536de2024-11-22 14:00:57 +090083}
84
85/// Set an instance ID of a virtual machine.
86///
87/// # Safety
88/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
89/// valid, non-null pointer to 64-byte data.
90#[no_mangle]
91pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
92 config: *mut VirtualMachineRawConfig,
93 instance_id: *const u8,
Inseob Kim06064252024-12-05 17:52:25 +090094 instance_id_size: usize,
Inseob Kimf3536de2024-11-22 14:00:57 +090095) -> c_int {
Inseob Kim06064252024-12-05 17:52:25 +090096 if instance_id_size != 64 {
97 return -libc::EINVAL;
98 }
99
Inseob Kimf3536de2024-11-22 14:00:57 +0900100 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
101 // AVirtualMachineRawConfig_create. It's the only reference to the object.
102 let config = unsafe { &mut *config };
103 // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
104 // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
105 // Both never overlap.
106 unsafe {
Inseob Kim06064252024-12-05 17:52:25 +0900107 ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), instance_id_size);
Inseob Kimf3536de2024-11-22 14:00:57 +0900108 }
109 0
110}
111
112/// Set a kernel image of a virtual machine.
113///
114/// # Safety
115/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
116/// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
117/// will be closed upon `AVirtualMachineRawConfig_delete`.
118#[no_mangle]
119pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
120 config: *mut VirtualMachineRawConfig,
121 fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900122) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900123 let file = get_file_from_fd(fd);
124 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
125 // AVirtualMachineRawConfig_create. It's the only reference to the object.
126 let config = unsafe { &mut *config };
127 config.kernel = file.map(ParcelFileDescriptor::new);
Inseob Kimf3536de2024-11-22 14:00:57 +0900128}
129
130/// Set an init rd of a virtual machine.
131///
132/// # Safety
133/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
134/// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
135/// will be closed upon `AVirtualMachineRawConfig_delete`.
136#[no_mangle]
137pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
138 config: *mut VirtualMachineRawConfig,
139 fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900140) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900141 let file = get_file_from_fd(fd);
142 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
143 // AVirtualMachineRawConfig_create. It's the only reference to the object.
144 let config = unsafe { &mut *config };
145 config.initrd = file.map(ParcelFileDescriptor::new);
Inseob Kimf3536de2024-11-22 14:00:57 +0900146}
147
148/// Add a disk for a virtual machine.
149///
150/// # Safety
151/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
152/// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
153/// closed upon `AVirtualMachineRawConfig_delete`.
154#[no_mangle]
155pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
156 config: *mut VirtualMachineRawConfig,
157 fd: c_int,
158 writable: bool,
159) -> c_int {
160 let file = get_file_from_fd(fd);
161 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
162 // AVirtualMachineRawConfig_create. It's the only reference to the object.
163 let config = unsafe { &mut *config };
164 match file {
165 // partition not supported yet
166 None => -libc::EINVAL,
167 Some(file) => {
168 config.disks.push(DiskImage {
169 image: Some(ParcelFileDescriptor::new(file)),
170 writable,
171 ..Default::default()
172 });
173 0
174 }
175 }
176}
177
178/// Set how much memory will be given to a virtual machine.
179///
180/// # Safety
181/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
182#[no_mangle]
Frederick Mayleb4030cf2024-12-13 15:05:54 -0800183pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMiB(
Inseob Kimf3536de2024-11-22 14:00:57 +0900184 config: *mut VirtualMachineRawConfig,
185 memory_mib: i32,
Inseob Kim06064252024-12-05 17:52:25 +0900186) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900187 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
188 // AVirtualMachineRawConfig_create. It's the only reference to the object.
189 let config = unsafe { &mut *config };
190 config.memoryMib = memory_mib;
Inseob Kimf3536de2024-11-22 14:00:57 +0900191}
192
Frederick Mayle384f5b52024-12-06 16:23:23 -0800193/// Set how much swiotlb will be given to a virtual machine.
194///
195/// # Safety
196/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
197#[no_mangle]
198pub unsafe extern "C" fn AVirtualMachineRawConfig_setSwiotlbMiB(
199 config: *mut VirtualMachineRawConfig,
200 swiotlb_mib: i32,
201) {
202 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
203 // AVirtualMachineRawConfig_create. It's the only reference to the object.
204 let config = unsafe { &mut *config };
205 config.swiotlbMib = swiotlb_mib;
206}
207
Frederick Maylecc0eb2b2024-12-11 19:38:16 -0800208/// Set vCPU count.
209///
210/// # Safety
211/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
212#[no_mangle]
213pub unsafe extern "C" fn AVirtualMachineRawConfig_setVCpuCount(
214 config: *mut VirtualMachineRawConfig,
215 n: i32,
216) {
217 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
218 // AVirtualMachineRawConfig_create. It's the only reference to the object.
219 let config = unsafe { &mut *config };
220 config.cpuTopology = CpuTopology::CUSTOM;
221 config.customVcpuCount = n;
222}
223
Inseob Kimf3536de2024-11-22 14:00:57 +0900224/// Set whether a virtual machine is protected or not.
225///
226/// # Safety
227/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
228#[no_mangle]
229pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
230 config: *mut VirtualMachineRawConfig,
231 protected_vm: bool,
Inseob Kim06064252024-12-05 17:52:25 +0900232) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900233 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
234 // AVirtualMachineRawConfig_create. It's the only reference to the object.
235 let config = unsafe { &mut *config };
236 config.protectedVm = protected_vm;
Inseob Kimf3536de2024-11-22 14:00:57 +0900237}
238
Inseob Kimf3536de2024-11-22 14:00:57 +0900239/// NOT IMPLEMENTED.
240///
241/// # Returns
242/// It always returns `-ENOTSUP`.
243#[no_mangle]
244pub extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
245 _config: *mut VirtualMachineRawConfig,
246 _enable: bool,
247) -> c_int {
248 -libc::ENOTSUP
249}
250
251/// NOT IMPLEMENTED.
252///
253/// # Returns
254/// It always returns `-ENOTSUP`.
255#[no_mangle]
256pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
257 _config: *mut VirtualMachineRawConfig,
258 _fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900259 _range_start: u64,
260 _range_end: u64,
Inseob Kimf3536de2024-11-22 14:00:57 +0900261) -> c_int {
262 -libc::ENOTSUP
263}
264
265/// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
266/// AIDL service, and connect to the child process.
267///
268/// # Safety
269/// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
270#[no_mangle]
271pub unsafe extern "C" fn AVirtualizationService_create(
272 service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
273 early: bool,
274) -> c_int {
275 let virtmgr =
276 if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
277 let virtmgr = match virtmgr {
278 Ok(virtmgr) => virtmgr,
279 Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
280 };
281 match virtmgr.connect() {
282 Ok(service) => {
283 // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
284 // pointer. `service` is the only reference here and `config` takes
285 // ownership.
286 unsafe {
287 *service_ptr = Box::into_raw(Box::new(service));
288 }
289 0
290 }
291 Err(_) => -libc::ECONNREFUSED,
292 }
293}
294
295/// Destroy a VirtualizationService object.
296///
297/// # Safety
298/// `service` must be a pointer returned by `AVirtualizationService_create` or
299/// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
300#[no_mangle]
301pub unsafe extern "C" fn AVirtualizationService_destroy(
302 service: *mut Strong<dyn IVirtualizationService>,
303) {
304 if !service.is_null() {
305 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
306 // `AVirtualizationService_create`. It's the only reference to the object.
307 unsafe {
308 let _ = Box::from_raw(service);
309 }
310 }
311}
312
313/// Create a virtual machine with given `config`.
314///
315/// # Safety
316/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
317/// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
318/// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
319/// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
320/// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
321#[no_mangle]
322pub unsafe extern "C" fn AVirtualMachine_createRaw(
323 service: *const Strong<dyn IVirtualizationService>,
324 config: *mut VirtualMachineRawConfig,
325 console_out_fd: c_int,
326 console_in_fd: c_int,
327 log_fd: c_int,
328 vm_ptr: *mut *mut VmInstance,
329) -> c_int {
330 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
331 // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
332 // reference to the object.
333 let service = unsafe { &*service };
334
335 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
336 // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
337 let config = unsafe { *Box::from_raw(config) };
338 let config = VirtualMachineConfig::RawConfig(config);
339
340 let console_out = get_file_from_fd(console_out_fd);
341 let console_in = get_file_from_fd(console_in_fd);
342 let log = get_file_from_fd(log_fd);
343
344 match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
345 Ok(vm) => {
346 // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
347 // `vm` is the only reference here and `vm_ptr` takes ownership.
348 unsafe {
349 *vm_ptr = Box::into_raw(Box::new(vm));
350 }
351 0
352 }
353 Err(_) => -libc::EIO,
354 }
355}
356
357/// Start a virtual machine.
358///
359/// # Safety
360/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
361#[no_mangle]
362pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
363 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
364 // `AVirtualMachine_createRaw`. It's the only reference to the object.
365 let vm = unsafe { &*vm };
366 match vm.start() {
367 Ok(_) => 0,
368 Err(_) => -libc::EIO,
369 }
370}
371
372/// Stop a virtual machine.
373///
374/// # Safety
375/// `vm` must be a pointer returned by `AVirtualMachine_create`.
376#[no_mangle]
377pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> c_int {
378 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
379 // `AVirtualMachine_createRaw`. It's the only reference to the object.
380 let vm = unsafe { &*vm };
381 match vm.stop() {
382 Ok(_) => 0,
383 Err(_) => -libc::EIO,
384 }
385}
386
Inseob Kim06064252024-12-05 17:52:25 +0900387fn death_reason_to_stop_reason(death_reason: DeathReason) -> AVirtualMachineStopReason {
388 match death_reason {
389 DeathReason::VirtualizationServiceDied => {
390 AVirtualMachineStopReason::AVIRTUAL_MACHINE_VIRTUALIZATION_SERVICE_DIED
391 }
392 DeathReason::InfrastructureError => {
393 AVirtualMachineStopReason::AVIRTUAL_MACHINE_INFRASTRUCTURE_ERROR
394 }
395 DeathReason::Killed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_KILLED,
396 DeathReason::Unknown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNKNOWN,
397 DeathReason::Shutdown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN,
398 DeathReason::StartFailed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_START_FAILED,
399 DeathReason::Reboot => AVirtualMachineStopReason::AVIRTUAL_MACHINE_REBOOT,
400 DeathReason::Crash => AVirtualMachineStopReason::AVIRTUAL_MACHINE_CRASH,
401 DeathReason::PvmFirmwarePublicKeyMismatch => {
402 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH
403 }
404 DeathReason::PvmFirmwareInstanceImageChanged => {
405 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
406 }
407 DeathReason::Hangup => AVirtualMachineStopReason::AVIRTUAL_MACHINE_HANGUP,
408 _ => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNRECOGNISED,
409 }
410}
411
412/// Wait until a virtual machine stops or the timeout elapses.
Inseob Kimf3536de2024-11-22 14:00:57 +0900413///
414/// # Safety
Inseob Kim06064252024-12-05 17:52:25 +0900415/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `timeout` must be a valid
416/// pointer to a `struct timespec` object or null. `reason` must be a valid, non-null pointer to an
417/// AVirtualMachineStopReason object.
Inseob Kimf3536de2024-11-22 14:00:57 +0900418#[no_mangle]
Inseob Kim06064252024-12-05 17:52:25 +0900419pub unsafe extern "C" fn AVirtualMachine_waitForStop(
420 vm: *const VmInstance,
421 timeout: *const timespec,
422 reason: *mut AVirtualMachineStopReason,
423) -> bool {
Inseob Kimf3536de2024-11-22 14:00:57 +0900424 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
425 // AVirtualMachine_create. It's the only reference to the object.
426 let vm = unsafe { &*vm };
Inseob Kim06064252024-12-05 17:52:25 +0900427
428 let death_reason = if timeout.is_null() {
429 vm.wait_for_death()
430 } else {
431 // SAFETY: `timeout` is assumed to be a valid pointer to a `struct timespec` object if
432 // non-null.
433 let timeout = unsafe { &*timeout };
434 let timeout = Duration::new(timeout.tv_sec as u64, timeout.tv_nsec as u32);
435 match vm.wait_for_death_with_timeout(timeout) {
436 Some(death_reason) => death_reason,
437 None => return false,
Inseob Kimf3536de2024-11-22 14:00:57 +0900438 }
Inseob Kim06064252024-12-05 17:52:25 +0900439 };
440
441 // SAFETY: `reason` is assumed to be a valid, non-null pointer to an
442 // AVirtualMachineStopReason object.
443 unsafe { *reason = death_reason_to_stop_reason(death_reason) };
444 true
Inseob Kimf3536de2024-11-22 14:00:57 +0900445}
446
447/// Destroy a virtual machine.
448///
449/// # Safety
450/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
451/// deletion.
452#[no_mangle]
453pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
454 if !vm.is_null() {
455 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
456 // AVirtualMachine_create. It's the only reference to the object.
457 unsafe {
458 let _ = Box::from_raw(vm);
459 }
460 }
461}
462
463fn get_file_from_fd(fd: i32) -> Option<File> {
464 if fd == -1 {
465 None
466 } else {
467 // SAFETY: transferring ownership of `fd` from the caller
468 Some(unsafe { File::from_raw_fd(fd) })
469 }
470}