blob: 5988fae4b313dd25f8289d48b196fd09b6d248be [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::{
26 DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
27 VirtualMachineConfig::VirtualMachineConfig,
28 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]
183pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMib(
184 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
193/// Set whether a virtual machine is protected or not.
194///
195/// # Safety
196/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
197#[no_mangle]
198pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
199 config: *mut VirtualMachineRawConfig,
200 protected_vm: bool,
Inseob Kim06064252024-12-05 17:52:25 +0900201) {
Inseob Kimf3536de2024-11-22 14:00:57 +0900202 // 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.protectedVm = protected_vm;
Inseob Kimf3536de2024-11-22 14:00:57 +0900206}
207
Inseob Kimf3536de2024-11-22 14:00:57 +0900208/// NOT IMPLEMENTED.
209///
210/// # Returns
211/// It always returns `-ENOTSUP`.
212#[no_mangle]
213pub extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
214 _config: *mut VirtualMachineRawConfig,
215 _enable: bool,
216) -> c_int {
217 -libc::ENOTSUP
218}
219
220/// NOT IMPLEMENTED.
221///
222/// # Returns
223/// It always returns `-ENOTSUP`.
224#[no_mangle]
225pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
226 _config: *mut VirtualMachineRawConfig,
227 _fd: c_int,
Inseob Kim06064252024-12-05 17:52:25 +0900228 _range_start: u64,
229 _range_end: u64,
Inseob Kimf3536de2024-11-22 14:00:57 +0900230) -> c_int {
231 -libc::ENOTSUP
232}
233
234/// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
235/// AIDL service, and connect to the child process.
236///
237/// # Safety
238/// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
239#[no_mangle]
240pub unsafe extern "C" fn AVirtualizationService_create(
241 service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
242 early: bool,
243) -> c_int {
244 let virtmgr =
245 if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
246 let virtmgr = match virtmgr {
247 Ok(virtmgr) => virtmgr,
248 Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
249 };
250 match virtmgr.connect() {
251 Ok(service) => {
252 // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
253 // pointer. `service` is the only reference here and `config` takes
254 // ownership.
255 unsafe {
256 *service_ptr = Box::into_raw(Box::new(service));
257 }
258 0
259 }
260 Err(_) => -libc::ECONNREFUSED,
261 }
262}
263
264/// Destroy a VirtualizationService object.
265///
266/// # Safety
267/// `service` must be a pointer returned by `AVirtualizationService_create` or
268/// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
269#[no_mangle]
270pub unsafe extern "C" fn AVirtualizationService_destroy(
271 service: *mut Strong<dyn IVirtualizationService>,
272) {
273 if !service.is_null() {
274 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
275 // `AVirtualizationService_create`. It's the only reference to the object.
276 unsafe {
277 let _ = Box::from_raw(service);
278 }
279 }
280}
281
282/// Create a virtual machine with given `config`.
283///
284/// # Safety
285/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
286/// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
287/// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
288/// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
289/// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
290#[no_mangle]
291pub unsafe extern "C" fn AVirtualMachine_createRaw(
292 service: *const Strong<dyn IVirtualizationService>,
293 config: *mut VirtualMachineRawConfig,
294 console_out_fd: c_int,
295 console_in_fd: c_int,
296 log_fd: c_int,
297 vm_ptr: *mut *mut VmInstance,
298) -> c_int {
299 // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
300 // `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
301 // reference to the object.
302 let service = unsafe { &*service };
303
304 // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
305 // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
306 let config = unsafe { *Box::from_raw(config) };
307 let config = VirtualMachineConfig::RawConfig(config);
308
309 let console_out = get_file_from_fd(console_out_fd);
310 let console_in = get_file_from_fd(console_in_fd);
311 let log = get_file_from_fd(log_fd);
312
313 match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
314 Ok(vm) => {
315 // SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
316 // `vm` is the only reference here and `vm_ptr` takes ownership.
317 unsafe {
318 *vm_ptr = Box::into_raw(Box::new(vm));
319 }
320 0
321 }
322 Err(_) => -libc::EIO,
323 }
324}
325
326/// Start a virtual machine.
327///
328/// # Safety
329/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
330#[no_mangle]
331pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
332 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
333 // `AVirtualMachine_createRaw`. It's the only reference to the object.
334 let vm = unsafe { &*vm };
335 match vm.start() {
336 Ok(_) => 0,
337 Err(_) => -libc::EIO,
338 }
339}
340
341/// Stop a virtual machine.
342///
343/// # Safety
344/// `vm` must be a pointer returned by `AVirtualMachine_create`.
345#[no_mangle]
346pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> c_int {
347 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
348 // `AVirtualMachine_createRaw`. It's the only reference to the object.
349 let vm = unsafe { &*vm };
350 match vm.stop() {
351 Ok(_) => 0,
352 Err(_) => -libc::EIO,
353 }
354}
355
Inseob Kim06064252024-12-05 17:52:25 +0900356fn death_reason_to_stop_reason(death_reason: DeathReason) -> AVirtualMachineStopReason {
357 match death_reason {
358 DeathReason::VirtualizationServiceDied => {
359 AVirtualMachineStopReason::AVIRTUAL_MACHINE_VIRTUALIZATION_SERVICE_DIED
360 }
361 DeathReason::InfrastructureError => {
362 AVirtualMachineStopReason::AVIRTUAL_MACHINE_INFRASTRUCTURE_ERROR
363 }
364 DeathReason::Killed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_KILLED,
365 DeathReason::Unknown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNKNOWN,
366 DeathReason::Shutdown => AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN,
367 DeathReason::StartFailed => AVirtualMachineStopReason::AVIRTUAL_MACHINE_START_FAILED,
368 DeathReason::Reboot => AVirtualMachineStopReason::AVIRTUAL_MACHINE_REBOOT,
369 DeathReason::Crash => AVirtualMachineStopReason::AVIRTUAL_MACHINE_CRASH,
370 DeathReason::PvmFirmwarePublicKeyMismatch => {
371 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH
372 }
373 DeathReason::PvmFirmwareInstanceImageChanged => {
374 AVirtualMachineStopReason::AVIRTUAL_MACHINE_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
375 }
376 DeathReason::Hangup => AVirtualMachineStopReason::AVIRTUAL_MACHINE_HANGUP,
377 _ => AVirtualMachineStopReason::AVIRTUAL_MACHINE_UNRECOGNISED,
378 }
379}
380
381/// Wait until a virtual machine stops or the timeout elapses.
Inseob Kimf3536de2024-11-22 14:00:57 +0900382///
383/// # Safety
Inseob Kim06064252024-12-05 17:52:25 +0900384/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `timeout` must be a valid
385/// pointer to a `struct timespec` object or null. `reason` must be a valid, non-null pointer to an
386/// AVirtualMachineStopReason object.
Inseob Kimf3536de2024-11-22 14:00:57 +0900387#[no_mangle]
Inseob Kim06064252024-12-05 17:52:25 +0900388pub unsafe extern "C" fn AVirtualMachine_waitForStop(
389 vm: *const VmInstance,
390 timeout: *const timespec,
391 reason: *mut AVirtualMachineStopReason,
392) -> bool {
Inseob Kimf3536de2024-11-22 14:00:57 +0900393 // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
394 // AVirtualMachine_create. It's the only reference to the object.
395 let vm = unsafe { &*vm };
Inseob Kim06064252024-12-05 17:52:25 +0900396
397 let death_reason = if timeout.is_null() {
398 vm.wait_for_death()
399 } else {
400 // SAFETY: `timeout` is assumed to be a valid pointer to a `struct timespec` object if
401 // non-null.
402 let timeout = unsafe { &*timeout };
403 let timeout = Duration::new(timeout.tv_sec as u64, timeout.tv_nsec as u32);
404 match vm.wait_for_death_with_timeout(timeout) {
405 Some(death_reason) => death_reason,
406 None => return false,
Inseob Kimf3536de2024-11-22 14:00:57 +0900407 }
Inseob Kim06064252024-12-05 17:52:25 +0900408 };
409
410 // SAFETY: `reason` is assumed to be a valid, non-null pointer to an
411 // AVirtualMachineStopReason object.
412 unsafe { *reason = death_reason_to_stop_reason(death_reason) };
413 true
Inseob Kimf3536de2024-11-22 14:00:57 +0900414}
415
416/// Destroy a virtual machine.
417///
418/// # Safety
419/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
420/// deletion.
421#[no_mangle]
422pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
423 if !vm.is_null() {
424 // 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 unsafe {
427 let _ = Box::from_raw(vm);
428 }
429 }
430}
431
432fn get_file_from_fd(fd: i32) -> Option<File> {
433 if fd == -1 {
434 None
435 } else {
436 // SAFETY: transferring ownership of `fd` from the caller
437 Some(unsafe { File::from_raw_fd(fd) })
438 }
439}