// Copyright 2021 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Provides parts of crosvm as a library to communicate with running crosvm instances.5//!6//! This crate is a programmatic alternative to invoking crosvm with subcommands that produce the7//! result on stdout.8//!9//! Downstream projects rely on this library maintaining a stable API surface.10//! Do not make changes to this library without consulting the crosvm externalization team.11//! Email: <[email protected]>12//!13//! The API of this library should remain the same regardless of which crosvm features are enabled.14//! Any missing functionality should be handled by returning an error at runtime, not conditional15//! compilation, so that users can rely on the the same set of functions with the same prototypes16//! regardless of how crosvm is configured.17//!18//! For more information see:19//! <https://crosvm.dev/book/running_crosvm/programmatic_interaction.html#usage>2021use std::convert::TryFrom;22use std::convert::TryInto;23use std::ffi::CStr;24use std::panic::catch_unwind;25use std::path::Path;26use std::path::PathBuf;27use std::time::Duration;2829use balloon_control::BalloonStats;30use balloon_control::BalloonWS;31use balloon_control::WSBucket;32use base::descriptor::IntoRawDescriptor;33use base::FromRawDescriptor;34use base::SafeDescriptor;35use libc::c_char;36use libc::c_int;37use libc::c_void;38use libc::ssize_t;39pub use swap::SwapStatus;40#[cfg(feature = "gpu")]41use vm_control::client::do_gpu_display_add;42#[cfg(feature = "gpu")]43use vm_control::client::do_gpu_display_list;44#[cfg(feature = "gpu")]45use vm_control::client::do_gpu_display_remove;46use vm_control::client::do_modify_battery;47use vm_control::client::do_net_add;48use vm_control::client::do_net_remove;49use vm_control::client::do_security_key_attach;50use vm_control::client::do_snd_mute_all;51use vm_control::client::do_usb_attach;52use vm_control::client::do_usb_detach;53use vm_control::client::do_usb_list;54use vm_control::client::handle_request;55use vm_control::client::handle_request_with_timeout;56use vm_control::client::vms_request;57#[cfg(feature = "gpu")]58use vm_control::gpu::DisplayMode;59#[cfg(feature = "gpu")]60use vm_control::gpu::DisplayParameters;61#[cfg(feature = "gpu")]62use vm_control::gpu::GpuControlResult;63use vm_control::BalloonControlCommand;64use vm_control::BatProperty;65use vm_control::DiskControlCommand;66use vm_control::HypervisorKind;67use vm_control::RegisteredEvent;68use vm_control::SwapCommand;69use vm_control::UsbControlAttachedDevice;70use vm_control::UsbControlResult;71use vm_control::VmRequest;72use vm_control::VmResponse;73use vm_control::USB_CONTROL_MAX_PORTS;7475#[repr(C)]76#[derive(Clone, zerocopy::FromZeros)]77pub struct CrosvmDisplayEntry {78pub id: u32,79pub width: u32,80pub height: u32,81pub refresh_rate: u32,82pub horizontal_dpi: u32,83pub vertical_dpi: u32,84}8586// Must match `VIRTIO_GPU_MAX_SCANOUTS` in `devices/src/virtio/gpu/protocol.rs`87pub const CROSVM_MAX_DISPLAYS: usize = 16;8889/// Simply returns the maximum possible number of GPU displays90#[no_mangle]91pub extern "C" fn crosvm_client_max_gpu_displays() -> usize {92CROSVM_MAX_DISPLAYS93}9495/// Adds a new display to the crosvm instance whose control socket is listening on `socket_path`.96///97/// The function returns true on success or false if an error occurred.98///99/// # Safety100///101/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a102/// C string that is valid for reads and not modified for the duration of the call.103#[no_mangle]104pub unsafe extern "C" fn crosvm_client_gpu_display_add(105socket_path: *const c_char,106width: u32,107height: u32,108refresh_rate: u32,109horizontal_dpi: u32,110vertical_dpi: u32,111) -> bool {112catch_unwind(|| {113#[cfg(feature = "gpu")]114{115let Some(socket_path) = validate_socket_path(socket_path) else {116return false;117};118let mode = DisplayMode::Windowed(width, height);119let params = DisplayParameters::new(120mode,121false, // hidden122refresh_rate,123horizontal_dpi,124vertical_dpi,125);126let Ok(result) = do_gpu_display_add(socket_path, vec![params]) else {127return false;128};129matches!(result, GpuControlResult::DisplaysUpdated)130}131#[cfg(not(feature = "gpu"))]132{133let _ = (134socket_path,135width,136height,137refresh_rate,138horizontal_dpi,139vertical_dpi,140);141false142}143})144.unwrap_or(false)145}146147/// Returns all displays attached to the crosvm instance whose control socket is listening on148/// `socket_path`.149///150/// The function returns the amount of entries written.151/// # Arguments152///153/// * `socket_path` - Path to the crosvm control socket154/// * `entries` - Pointer to an array of `CrosvmDisplayEntry` where the details about the attached155/// displays will be written to156/// * `entries_length` - Amount of entries in the array specified by `entries`157///158/// # Safety159///160/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C161/// string that is valid and for reads and not modified for the duration of the call. `entries`162/// should be a valid pointer to an array of `CrosvmDisplayEntry` valid for writes that contains at163/// least `entries_length` elements and is not externally modified for the duration of this call.164#[no_mangle]165pub unsafe extern "C" fn crosvm_client_gpu_display_list(166socket_path: *const c_char,167entries: *mut CrosvmDisplayEntry,168entries_length: ssize_t,169) -> ssize_t {170catch_unwind(|| {171#[cfg(feature = "gpu")]172{173if entries.is_null() {174return -1;175}176let Some(socket_path) = validate_socket_path(socket_path) else {177return -1;178};179let Ok(GpuControlResult::DisplayList { displays }) = do_gpu_display_list(socket_path)180else {181return -1;182};183184let limit = usize::try_from(entries_length).unwrap_or(0);185if limit == 0 {186return 0;187}188let cnt = limit.min(displays.len());189// SAFETY: checked that `entries` is not null and `limit` is less than or equal to190// `entries_length`.191let entries_slice = unsafe {192std::slice::from_raw_parts_mut(entries as *mut CrosvmDisplayEntry, limit)193};194for (entry, (id, params)) in entries_slice.iter_mut().zip(displays) {195let (width, height) = params.get_window_size();196*entry = CrosvmDisplayEntry {197id,198width,199height,200refresh_rate: params.refresh_rate,201horizontal_dpi: params.horizontal_dpi(),202vertical_dpi: params.vertical_dpi(),203};204}205cnt as ssize_t206}207#[cfg(not(feature = "gpu"))]208{209let _ = (socket_path, entries, entries_length);210-1211}212})213.unwrap_or(-1)214}215216/// Removes a display from the crosvm instance whose control socket is listening on `socket_path`.217///218/// The function returns true on success or false if an error occurred.219///220/// # Safety221///222/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a223/// C string that is valid for reads and not modified for the duration of the call.224#[no_mangle]225pub unsafe extern "C" fn crosvm_client_gpu_display_remove(226socket_path: *const c_char,227display_id: u32,228) -> bool {229catch_unwind(|| {230#[cfg(feature = "gpu")]231{232let Some(socket_path) = validate_socket_path(socket_path) else {233return false;234};235let Ok(result) = do_gpu_display_remove(socket_path, vec![display_id]) else {236return false;237};238matches!(result, GpuControlResult::DisplaysUpdated)239}240#[cfg(not(feature = "gpu"))]241{242let _ = (socket_path, display_id);243false244}245})246.unwrap_or(false)247}248249pub const VIRTIO_BALLOON_WS_MAX_NUM_BINS: usize = 16;250pub const VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS: usize = 15;251252/// # Safety253///254/// This function is safe when the caller ensures the socket_path raw pointer can be safely passed255/// to `CStr::from_ptr()`.256unsafe fn validate_socket_path(socket_path: *const c_char) -> Option<PathBuf> {257if !socket_path.is_null() {258let socket_path = CStr::from_ptr(socket_path);259Some(PathBuf::from(socket_path.to_str().ok()?))260} else {261None262}263}264265/// Stops the crosvm instance whose control socket is listening on `socket_path`.266///267/// The function returns true on success or false if an error occurred.268///269/// # Safety270///271/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a272/// C string that is valid and not modified for the duration of the call.273#[no_mangle]274pub unsafe extern "C" fn crosvm_client_stop_vm(socket_path: *const c_char) -> bool {275catch_unwind(|| {276if let Some(socket_path) = validate_socket_path(socket_path) {277vms_request(&VmRequest::Exit, socket_path).is_ok()278} else {279false280}281})282.unwrap_or(false)283}284285/// Suspends the crosvm instance whose control socket is listening on `socket_path`.286///287/// The function returns true on success or false if an error occurred.288///289/// # Safety290///291/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a292/// C string that is valid and not modified for the duration of the call.293#[no_mangle]294pub unsafe extern "C" fn crosvm_client_suspend_vm(socket_path: *const c_char) -> bool {295catch_unwind(|| {296if let Some(socket_path) = validate_socket_path(socket_path) {297vms_request(&VmRequest::SuspendVcpus, socket_path).is_ok()298} else {299false300}301})302.unwrap_or(false)303}304305/// Resumes the crosvm instance whose control socket is listening on `socket_path`.306///307/// Note: this function just resumes vcpus of the vm. If you need to perform a full resume, call308/// crosvm_client_resume_vm_full.309///310/// The function returns true on success or false if an error occurred.311///312/// # Safety313///314/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a315/// C string that is valid for reads and not modified for the duration of the call.316#[no_mangle]317pub unsafe extern "C" fn crosvm_client_resume_vm(socket_path: *const c_char) -> bool {318catch_unwind(|| {319if let Some(socket_path) = validate_socket_path(socket_path) {320vms_request(&VmRequest::ResumeVcpus, socket_path).is_ok()321} else {322false323}324})325.unwrap_or(false)326}327328/// Resumes the crosvm instance whose control socket is listening on `socket_path`.329///330/// Note: unlike crosvm_client_resume_vm, this function resumes both vcpus and devices.331///332/// The function returns true on success or false if an error occurred.333///334/// # Safety335///336/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a337/// C string that is valid for reads and not modified for the duration of the call.338#[no_mangle]339pub unsafe extern "C" fn crosvm_client_resume_vm_full(socket_path: *const c_char) -> bool {340catch_unwind(|| {341if let Some(socket_path) = validate_socket_path(socket_path) {342vms_request(&VmRequest::ResumeVm, socket_path).is_ok()343} else {344false345}346})347.unwrap_or(false)348}349350/// Creates an RT vCPU for the crosvm instance whose control socket is listening on `socket_path`.351///352/// The function returns true on success or false if an error occurred.353///354/// # Safety355///356/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a357/// C string that is valid for reads and not modified for the duration of the call.358#[no_mangle]359pub unsafe extern "C" fn crosvm_client_make_rt_vm(socket_path: *const c_char) -> bool {360catch_unwind(|| {361if let Some(socket_path) = validate_socket_path(socket_path) {362vms_request(&VmRequest::MakeRT, socket_path).is_ok()363} else {364false365}366})367.unwrap_or(false)368}369370/// Adjusts the balloon size of the crosvm instance whose control socket is371/// listening on `socket_path`.372///373/// The function returns true on success or false if an error occurred.374///375/// # Safety376///377/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a378/// C string that is valid for reads and not modified for the duration of the call.379#[no_mangle]380pub unsafe extern "C" fn crosvm_client_balloon_vms(381socket_path: *const c_char,382num_bytes: u64,383) -> bool {384catch_unwind(|| {385if let Some(socket_path) = validate_socket_path(socket_path) {386let command = BalloonControlCommand::Adjust {387num_bytes,388wait_for_success: false,389};390vms_request(&VmRequest::BalloonCommand(command), socket_path).is_ok()391} else {392false393}394})395.unwrap_or(false)396}397398/// See crosvm_client_balloon_vms.399///400/// # Safety401///402/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a403/// C string that is valid for reads and not modified for the duration of the call.404#[no_mangle]405pub unsafe extern "C" fn crosvm_client_balloon_vms_wait_with_timeout(406socket_path: *const c_char,407num_bytes: u64,408timeout_ms: u64,409) -> bool {410catch_unwind(|| {411if let Some(socket_path) = validate_socket_path(socket_path) {412let command = BalloonControlCommand::Adjust {413num_bytes,414wait_for_success: true,415};416let resp = handle_request_with_timeout(417&VmRequest::BalloonCommand(command),418socket_path,419Some(Duration::from_millis(timeout_ms)),420);421if matches!(resp, Ok(VmResponse::Ok)) {422return true;423}424println!("adjust failure: {resp:?}");425}426false427})428.unwrap_or(false)429}430431/// Mute or unmute all snd devices of the crosvm instance whose control socket is432/// listening on `socket_path`.433///434/// The function returns true on success or false if an error occurred.435///436/// # Safety437///438/// The caller will ensure the raw pointers in arguments passed in can be safely used by439/// `CStr::from_ptr()`440#[no_mangle]441pub unsafe extern "C" fn crosvm_client_snd_mute_all(442socket_path: *const c_char,443muted: bool,444) -> bool {445catch_unwind(|| {446if let Some(socket_path) = validate_socket_path(socket_path) {447do_snd_mute_all(socket_path, muted).is_ok()448} else {449false450}451})452.unwrap_or(false)453}454455/// Enable vmm swap for crosvm instance whose control socket is listening on `socket_path`.456///457/// The function returns true on success or false if an error occurred.458///459/// # Safety460///461/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a462/// C string that is valid for reads and not modified for the duration of the call.463#[no_mangle]464pub unsafe extern "C" fn crosvm_client_swap_enable_vm(socket_path: *const c_char) -> bool {465catch_unwind(|| {466if let Some(socket_path) = validate_socket_path(socket_path) {467vms_request(&VmRequest::Swap(SwapCommand::Enable), socket_path).is_ok()468} else {469false470}471})472.unwrap_or(false)473}474475/// Swap out staging memory for crosvm instance whose control socket is listening476/// on `socket_path`.477///478/// The function returns true on success or false if an error occurred.479///480/// # Safety481///482/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a483/// C string that is valid for reads and not modified for the duration of the call.484#[no_mangle]485pub unsafe extern "C" fn crosvm_client_swap_swapout_vm(socket_path: *const c_char) -> bool {486catch_unwind(|| {487if let Some(socket_path) = validate_socket_path(socket_path) {488vms_request(&VmRequest::Swap(SwapCommand::SwapOut), socket_path).is_ok()489} else {490false491}492})493.unwrap_or(false)494}495496/// Arguments structure for crosvm_client_swap_disable_vm2.497#[repr(C)]498pub struct SwapDisableArgs {499/// The path of the control socket to target.500pub socket_path: *const c_char,501/// Whether or not the swap file should be cleaned up in the background.502pub slow_file_cleanup: bool,503}504505/// Disable vmm swap according to `args`.506///507/// The function returns true on success or false if an error occurred.508///509/// # Safety510///511/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a512/// `SwapDisableArgs` instance valid for writes that is not externally modified for the duration of513/// this call.514#[no_mangle]515pub unsafe extern "C" fn crosvm_client_swap_disable_vm(args: *mut SwapDisableArgs) -> bool {516catch_unwind(|| {517if args.is_null() {518return false;519}520let Some(socket_path) = validate_socket_path((*args).socket_path) else {521return false;522};523vms_request(524&VmRequest::Swap(SwapCommand::Disable {525slow_file_cleanup: (*args).slow_file_cleanup,526}),527socket_path,528)529.is_ok()530})531.unwrap_or(false)532}533534/// Trim staging memory for vmm swap for crosvm instance whose control socket is listening on535/// `socket_path`.536///537/// The function returns true on success or false if an error occurred.538///539/// # Safety540///541/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a542/// C string that is valid for reads and not modified for the duration of the call.543#[no_mangle]544pub unsafe extern "C" fn crosvm_client_swap_trim(socket_path: *const c_char) -> bool {545catch_unwind(|| {546if let Some(socket_path) = validate_socket_path(socket_path) {547vms_request(&VmRequest::Swap(SwapCommand::Trim), socket_path).is_ok()548} else {549false550}551})552.unwrap_or(false)553}554555/// Returns vmm-swap status of the crosvm instance whose control socket is listening on556/// `socket_path`.557///558/// The parameters `status` is optional and will only be written to if they are non-null.559///560/// The function returns true on success or false if an error occurred.561///562/// # Safety563///564/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C565/// string that is valid for reads and not modified for the duration of the call, and that `status`566/// is a non-null pointer to a `SwapStatus` valid for writes that is not externally modified for567/// the duration of the call.568#[no_mangle]569pub unsafe extern "C" fn crosvm_client_swap_status(570socket_path: *const c_char,571status: *mut SwapStatus,572) -> bool {573catch_unwind(|| {574if let Some(socket_path) = validate_socket_path(socket_path) {575let request = &VmRequest::Swap(SwapCommand::Status);576if let Ok(VmResponse::SwapStatus(response)) = handle_request(request, socket_path) {577if !status.is_null() {578// SAFETY: just checked that `status` is not null.579unsafe {580*status = response;581}582}583true584} else {585false586}587} else {588false589}590})591.unwrap_or(false)592}593594/// Represents an individual attached USB device.595#[repr(C)]596pub struct UsbDeviceEntry {597/// Internal port index used for identifying this individual device.598pub port: u8,599/// USB vendor ID600pub vendor_id: u16,601/// USB product ID602pub product_id: u16,603}604605impl From<&UsbControlAttachedDevice> for UsbDeviceEntry {606fn from(other: &UsbControlAttachedDevice) -> Self {607Self {608port: other.port,609vendor_id: other.vendor_id,610product_id: other.product_id,611}612}613}614615/// Simply returns the maximum possible number of USB devices616#[no_mangle]617pub extern "C" fn crosvm_client_max_usb_devices() -> usize {618USB_CONTROL_MAX_PORTS619}620621/// Returns all USB devices passed through the crosvm instance whose control socket is listening on622/// `socket_path`.623///624/// The function returns the amount of entries written.625/// # Arguments626///627/// * `socket_path` - Path to the crosvm control socket628/// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached629/// devices will be written to630/// * `entries_length` - Amount of entries in the array specified by `entries`631///632/// Use the value returned by [`crosvm_client_max_usb_devices()`] to determine the size of the input633/// array to this function.634///635/// # Safety636///637/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C638/// string that is valid and for reads and not modified for the duration of the call. `entries`639/// should be a valid pointer to an array of `UsbDeviceEntry` valid for writes that contains at640/// least `entries_length` elements and is not externally modified for the duration of this call.641#[no_mangle]642pub unsafe extern "C" fn crosvm_client_usb_list(643socket_path: *const c_char,644entries: *mut UsbDeviceEntry,645entries_length: ssize_t,646) -> ssize_t {647catch_unwind(|| {648if let Some(socket_path) = validate_socket_path(socket_path) {649if entries.is_null() {650return -1;651}652if let Ok(UsbControlResult::Devices(res)) = do_usb_list(socket_path) {653let mut i = 0;654for entry in res.iter().filter(|x| x.valid()) {655if i >= entries_length {656break;657}658// SAFETY: checked that `entries` is not null.659unsafe {660*entries.offset(i) = entry.into();661i += 1;662}663}664i665} else {666-1667}668} else {669-1670}671})672.unwrap_or(-1)673}674675/// Attaches an USB device to crosvm instance whose control socket is listening on `socket_path`.676///677/// The function returns the amount of entries written.678/// # Arguments679///680/// * `socket_path` - Path to the crosvm control socket681/// * `bus` - USB device bus ID (unused)682/// * `addr` - USB device address (unused)683/// * `vid` - USB device vendor ID (unused)684/// * `pid` - USB device product ID (unused)685/// * `dev_path` - Path to the USB device (Most likely `/dev/bus/usb/<bus>/<addr>`).686/// * `out_port` - (optional) internal port will be written here if provided.687///688/// The function returns true on success or false if an error occurred.689///690/// # Safety691///692/// Function is unsafe due to raw pointer usage.693/// Trivial !raw_pointer.is_null() checks prevent some unsafe behavior, but the caller should694/// ensure no null pointers are passed into the function.695///696/// The safety requirements for `socket_path` and `dev_path` are the same as the ones from697/// `CStr::from_ptr()`. `out_port` should be a non-null pointer that points to a writable 1byte698/// region.699#[no_mangle]700pub unsafe extern "C" fn crosvm_client_usb_attach(701socket_path: *const c_char,702_bus: u8,703_addr: u8,704_vid: u16,705_pid: u16,706dev_path: *const c_char,707out_port: *mut u8,708) -> bool {709catch_unwind(|| {710if let Some(socket_path) = validate_socket_path(socket_path) {711if dev_path.is_null() {712return false;713}714// SAFETY: just checked that `dev_path` is not null.715let dev_path = Path::new(unsafe { CStr::from_ptr(dev_path) }.to_str().unwrap_or(""));716717if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(socket_path, dev_path) {718if !out_port.is_null() {719// SAFETY: trivially safe720unsafe { *out_port = port };721}722true723} else {724false725}726} else {727false728}729})730.unwrap_or(false)731}732733/// Attaches a u2f security key to crosvm instance whose control socket is listening on734/// `socket_path`.735///736/// The function returns the amount of entries written.737/// # Arguments738///739/// * `socket_path` - Path to the crosvm control socket740/// * `hidraw_path` - Path to the hidraw device of the security key (like `/dev/hidraw0`)741/// * `out_port` - (optional) internal port will be written here if provided.742///743/// The function returns true on success or false if an error occurred.744///745/// # Safety746///747/// Function is unsafe due to raw pointer usage.748/// Trivial !raw_pointer.is_null() checks prevent some unsafe behavior, but the caller should749/// ensure no null pointers are passed into the function.750///751/// The safety requirements for `socket_path` and `hidraw_path` are the same as the ones from752/// `CStr::from_ptr()`. `out_port` should be a non-null pointer that points to a writable 1byte753/// region.754#[no_mangle]755pub unsafe extern "C" fn crosvm_client_security_key_attach(756socket_path: *const c_char,757hidraw_path: *const c_char,758out_port: *mut u8,759) -> bool {760catch_unwind(|| {761if let Some(socket_path) = validate_socket_path(socket_path) {762if hidraw_path.is_null() {763return false;764}765let hidraw_path = Path::new(766// SAFETY: just checked that `hidraw_path` is not null.767unsafe { CStr::from_ptr(hidraw_path) }768.to_str()769.unwrap_or(""),770);771772if let Ok(UsbControlResult::Ok { port }) =773do_security_key_attach(socket_path, hidraw_path)774{775if !out_port.is_null() {776// SAFETY: trivially safe777unsafe { *out_port = port };778}779true780} else {781false782}783} else {784false785}786})787.unwrap_or(false)788}789790/// Detaches an USB device from crosvm instance whose control socket is listening on `socket_path`.791/// `port` determines device to be detached.792///793/// The function returns true on success or false if an error occurred.794///795/// # Safety796///797/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a798/// C string that is valid for reads and not modified for the duration of the call.799#[no_mangle]800pub unsafe extern "C" fn crosvm_client_usb_detach(socket_path: *const c_char, port: u8) -> bool {801catch_unwind(|| {802if let Some(socket_path) = validate_socket_path(socket_path) {803do_usb_detach(socket_path, port).is_ok()804} else {805false806}807})808.unwrap_or(false)809}810811/// Attaches a net tap device to the crosvm instance with control socket at `socket_path`.812///813/// # Arguments814///815/// * `socket_path` - Path to the crosvm control socket816/// * `tap_name` - Name of the tap device817/// * `out_bus_num` - guest bus number will be written here818///819/// The function returns true on success, false on failure.820///821/// # Safety822///823/// Function is unsafe due to raw pointer usage - socket_path and tap_name are assumed to point to a824/// null-terminated CStr. Function checks that the pointers are not null, but caller need to check825/// the validity of the pointer. out_bus_num is assumed to point to a u8 integer.826#[no_mangle]827pub unsafe extern "C" fn crosvm_client_net_tap_attach(828socket_path: *const c_char,829tap_name: *const c_char,830out_bus_num: *mut u8,831) -> bool {832catch_unwind(|| {833if let Some(socket_path) = validate_socket_path(socket_path) {834if tap_name.is_null() || out_bus_num.is_null() {835return false;836}837// SAFETY: just checked that `tap_name` is not null. Function caller guarantees it838// points to a valid CStr.839let tap_name = unsafe { CStr::from_ptr(tap_name) }.to_str().unwrap_or("");840841match do_net_add(tap_name, socket_path) {842Ok(bus_num) => {843// SAFETY: checked out_bus_num is not null. Function caller guarantees844// validity of pointer.845unsafe { *out_bus_num = bus_num };846true847}848Err(_e) => false,849}850} else {851false852}853})854.unwrap_or(false)855}856857/// Detaches a hotplugged tap device from the crosvm instance with control socket at `socket_path`.858///859/// # Arguments860///861/// * `socket_path` - Path to the crosvm control socket862/// * `bus_num` - Bus number of the tap device to be removed.863///864/// The function returns true on success, and false on failure.865///866/// # Safety867///868/// Function is unsafe due to raw pointer usage - socket_path is assumed to point to a869/// null-terminated Cstr. Function checks that the pointers are not null, but caller need to check870/// the validity of the pointer.871#[no_mangle]872pub unsafe extern "C" fn crosvm_client_net_tap_detach(873socket_path: *const c_char,874bus_num: u8,875) -> bool {876catch_unwind(|| {877if let Some(socket_path) = validate_socket_path(socket_path) {878match do_net_remove(bus_num, socket_path) {879Ok(()) => true,880Err(_e) => false,881}882} else {883false884}885})886.unwrap_or(false)887}888889/// Modifies the battery status of crosvm instance whose control socket is listening on890/// `socket_path`.891///892/// The function returns true on success or false if an error occurred.893///894/// # Safety895///896/// The caller will ensure the raw pointers in arguments passed in can be safely used by897/// `CStr::from_ptr()`898#[no_mangle]899pub unsafe extern "C" fn crosvm_client_modify_battery(900socket_path: *const c_char,901battery_type: *const c_char,902property: *const c_char,903target: *const c_char,904) -> bool {905catch_unwind(|| {906if let Some(socket_path) = validate_socket_path(socket_path) {907if battery_type.is_null() || property.is_null() || target.is_null() {908return false;909}910// SAFETY: trivially safe911let battery_type = unsafe { CStr::from_ptr(battery_type) };912// SAFETY: trivially safe913let property = unsafe { CStr::from_ptr(property) };914// SAFETY: trivially safe915let target = unsafe { CStr::from_ptr(target) };916917do_modify_battery(918socket_path,919battery_type.to_str().unwrap(),920property.to_str().unwrap(),921target.to_str().unwrap(),922)923.is_ok()924} else {925false926}927})928.unwrap_or(false)929}930931/// Fakes the battery status of crosvm instance. The power status will always be on932/// battery, and the maximum battery capacity could be read by guest is set to the933/// `max_battery_capacity`.934///935/// The function returns true on success or false if an error occurred.936///937/// # Arguments938///939/// * `socket_path` - Path to the crosvm control socket940/// * `battery_type` - Type of battery emulation corresponding to vm_tools::BatteryType941/// * `max_battery_capacity` - maximum battery capacity could be read by guest942///943/// # Safety944///945/// The caller will ensure the raw pointers in arguments passed in can be safely used by946/// `CStr::from_ptr()`947#[no_mangle]948pub unsafe extern "C" fn crosvm_client_fake_power(949socket_path: *const c_char,950battery_type: *const c_char,951max_battery_capacity: u32,952) -> bool {953catch_unwind(|| {954if let Some(socket_path) = validate_socket_path(socket_path) {955if battery_type.is_null() || max_battery_capacity > 100 {956return false;957}958959let battery_type = CStr::from_ptr(battery_type);960let fake_max_capacity_target: String = max_battery_capacity.to_string();961962do_modify_battery(963socket_path.clone(),964battery_type.to_str().unwrap(),965&BatProperty::SetFakeBatConfig.to_string(),966fake_max_capacity_target.as_str(),967)968.is_ok()969} else {970false971}972})973.unwrap_or(false)974}975976/// Resume the battery status of crosvm instance from fake status977///978/// The function returns true on success or false if an error occurred.979///980/// # Arguments981///982/// * `socket_path` - Path to the crosvm control socket983/// * `battery_type` - Type of battery emulation corresponding to vm_tools::BatteryType984///985/// # Safety986///987/// The caller will ensure the raw pointers in arguments passed in can be safely used by988/// `CStr::from_ptr()`.989#[no_mangle]990pub unsafe extern "C" fn crosvm_client_cancel_fake_power(991socket_path: *const c_char,992battery_type: *const c_char,993) -> bool {994catch_unwind(|| {995if let Some(socket_path) = validate_socket_path(socket_path) {996if battery_type.is_null() {997return false;998}9991000// SAFETY: the caller has a responsibility of giving a valid char* pointer1001let battery_type = CStr::from_ptr(battery_type);10021003do_modify_battery(1004socket_path,1005battery_type.to_str().unwrap(),1006&BatProperty::CancelFakeBatConfig.to_string(),1007"",1008)1009.is_ok()1010} else {1011false1012}1013})1014.unwrap_or(false)1015}10161017/// Resizes the disk of the crosvm instance whose control socket is listening on `socket_path`.1018///1019/// The function returns true on success or false if an error occurred.1020///1021/// # Safety1022///1023/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a1024/// C string that is valid for reads and not modified for the duration of the call.1025#[no_mangle]1026pub unsafe extern "C" fn crosvm_client_resize_disk(1027socket_path: *const c_char,1028disk_index: u64,1029new_size: u64,1030) -> bool {1031catch_unwind(|| {1032if let Some(socket_path) = validate_socket_path(socket_path) {1033if let Ok(disk_index) = usize::try_from(disk_index) {1034let request = VmRequest::DiskCommand {1035disk_index,1036command: DiskControlCommand::Resize { new_size },1037};1038vms_request(&request, socket_path).is_ok()1039} else {1040false1041}1042} else {1043false1044}1045})1046.unwrap_or(false)1047}10481049/// Similar to internally used `BalloonStats` but using `i64` instead of1050/// `Option<u64>`. `None` (or values bigger than `i64::max`) will be encoded as -1.1051#[repr(C)]1052pub struct BalloonStatsFfi {1053pub swap_in: i64,1054pub swap_out: i64,1055pub major_faults: i64,1056pub minor_faults: i64,1057pub free_memory: i64,1058pub total_memory: i64,1059pub available_memory: i64,1060pub disk_caches: i64,1061pub hugetlb_allocations: i64,1062pub hugetlb_failures: i64,1063pub shared_memory: i64,1064pub unevictable_memory: i64,1065}10661067impl From<&BalloonStats> for BalloonStatsFfi {1068fn from(other: &BalloonStats) -> Self {1069let convert = |x: Option<u64>| -> i64 { x.and_then(|y| y.try_into().ok()).unwrap_or(-1) };1070Self {1071swap_in: convert(other.swap_in),1072swap_out: convert(other.swap_out),1073major_faults: convert(other.major_faults),1074minor_faults: convert(other.minor_faults),1075free_memory: convert(other.free_memory),1076total_memory: convert(other.total_memory),1077available_memory: convert(other.available_memory),1078disk_caches: convert(other.disk_caches),1079hugetlb_allocations: convert(other.hugetlb_allocations),1080hugetlb_failures: convert(other.hugetlb_failures),1081shared_memory: convert(other.shared_memory),1082unevictable_memory: convert(other.unevictable_memory),1083}1084}1085}10861087/// Returns balloon stats of the crosvm instance whose control socket is listening on `socket_path`.1088///1089/// The parameters `stats` and `actual` are optional and will only be written to if they are1090/// non-null.1091///1092/// The function returns true on success or false if an error occurred.1093///1094/// # Note1095///1096/// Entries in `BalloonStatsFfi` that are not available will be set to `-1`.1097///1098/// # Safety1099///1100/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1101/// string that is valid for reads and not modified for the duration of the call. `stats` should be1102/// a pointer to a `BalloonStatsFfi` valid for writes that is not modified for the duration of this1103/// call, and `actual` should be a pointer to a `u64` valid for writes that is not modified for the1104/// duration of this call.1105#[no_mangle]1106pub unsafe extern "C" fn crosvm_client_balloon_stats(1107socket_path: *const c_char,1108stats: *mut BalloonStatsFfi,1109actual: *mut u64,1110) -> bool {1111crosvm_client_balloon_stats_impl(socket_path, None, stats, actual)1112}11131114/// See crosvm_client_balloon_stats.1115///1116/// # Safety1117///1118/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1119/// string that is valid for reads and not modified for the duration of the call. `stats` should be1120/// a pointer to a `BalloonStatsFfi` valid for writes is not modified for the duration of this1121/// call, and `actual` should be a pointer to a `u64` valid for writes that is not modified for the1122/// duration of this call.1123#[no_mangle]1124pub unsafe extern "C" fn crosvm_client_balloon_stats_with_timeout(1125socket_path: *const c_char,1126timeout_ms: u64,1127stats: *mut BalloonStatsFfi,1128actual: *mut u64,1129) -> bool {1130crosvm_client_balloon_stats_impl(1131socket_path,1132Some(Duration::from_millis(timeout_ms)),1133stats,1134actual,1135)1136}11371138/// # Safety1139///1140/// This function is safe when the caller ensures the socket_path raw pointer can be safely passed1141/// to `CStr::from_ptr()`. `stats` should be a pointer to a `BalloonStatsFfi` valid for writes that1142/// is not modified for the duration of this call, and `actual` should be a pointer to a `u64`1143/// valid for writes that is not modified for the duration of this call.1144unsafe fn crosvm_client_balloon_stats_impl(1145socket_path: *const c_char,1146timeout_ms: Option<Duration>,1147stats: *mut BalloonStatsFfi,1148actual: *mut u64,1149) -> bool {1150catch_unwind(|| {1151if let Some(socket_path) = validate_socket_path(socket_path) {1152let request = &VmRequest::BalloonCommand(BalloonControlCommand::Stats {});1153let resp = handle_request_with_timeout(request, socket_path, timeout_ms);1154if let Ok(VmResponse::BalloonStats {1155stats: ref balloon_stats,1156balloon_actual,1157}) = resp1158{1159if !stats.is_null() {1160// SAFETY: just checked that `stats` is not null.1161unsafe {1162*stats = balloon_stats.into();1163}1164}11651166if !actual.is_null() {1167// SAFETY: just checked that `actual` is not null.1168unsafe {1169*actual = balloon_actual;1170}1171}1172true1173} else {1174false1175}1176} else {1177false1178}1179})1180.unwrap_or(false)1181}11821183/// Externally exposed variant of BalloonWS/WSBucket, used for FFI.1184#[derive(Clone, Copy, Debug)]1185#[repr(C)]1186pub struct WorkingSetBucketFfi {1187pub age: u64,1188pub bytes: [u64; 2],1189}11901191impl WorkingSetBucketFfi {1192fn new() -> Self {1193Self {1194age: 0,1195bytes: [0, 0],1196}1197}1198}11991200impl From<WSBucket> for WorkingSetBucketFfi {1201fn from(other: WSBucket) -> Self {1202Self {1203age: other.age,1204bytes: other.bytes,1205}1206}1207}12081209#[repr(C)]1210#[derive(Debug)]1211pub struct BalloonWSFfi {1212pub ws: [WorkingSetBucketFfi; VIRTIO_BALLOON_WS_MAX_NUM_BINS],1213pub num_bins: u8,1214pub _reserved: [u8; 7],1215}12161217impl TryFrom<&BalloonWS> for BalloonWSFfi {1218type Error = &'static str;12191220fn try_from(value: &BalloonWS) -> Result<Self, Self::Error> {1221if value.ws.len() > VIRTIO_BALLOON_WS_MAX_NUM_BINS {1222return Err("too many WS buckets in source object.");1223}12241225let mut ffi = Self {1226ws: [WorkingSetBucketFfi::new(); VIRTIO_BALLOON_WS_MAX_NUM_BINS],1227num_bins: value.ws.len() as u8,1228..Default::default()1229};1230for (ffi_ws, other_ws) in ffi.ws.iter_mut().zip(value.ws.iter()) {1231*ffi_ws = (*other_ws).into();1232}1233Ok(ffi)1234}1235}12361237impl BalloonWSFfi {1238pub fn new() -> Self {1239Self {1240ws: [WorkingSetBucketFfi::new(); VIRTIO_BALLOON_WS_MAX_NUM_BINS],1241num_bins: 0,1242_reserved: [0; 7],1243}1244}1245}12461247impl Default for BalloonWSFfi {1248fn default() -> Self {1249Self::new()1250}1251}12521253#[repr(C)]1254pub struct BalloonWSRConfigFfi {1255pub intervals: [u64; VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS],1256pub num_intervals: u8,1257pub _reserved: [u8; 7],1258pub refresh_threshold: u64,1259pub report_threshold: u64,1260}12611262/// Returns balloon working set of the crosvm instance whose control socket is listening on1263/// socket_path.1264///1265/// The function returns true on success or false if an error occurred.1266///1267/// # Safety1268///1269/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1270/// string that is valid for reads and not modified for the duration of the call. `ws` and `actual`1271/// should be pointers to a `BalloonStatsFfi` and `u64` respectively that are valid for writes and1272/// not modified for the duration of this call.1273#[no_mangle]1274pub unsafe extern "C" fn crosvm_client_balloon_working_set(1275socket_path: *const c_char,1276ws: *mut BalloonWSFfi,1277actual: *mut u64,1278) -> bool {1279catch_unwind(|| {1280if let Some(socket_path) = validate_socket_path(socket_path) {1281let request = &VmRequest::BalloonCommand(BalloonControlCommand::WorkingSet);1282if let Ok(VmResponse::BalloonWS {1283ws: ref balloon_ws,1284balloon_actual,1285}) = handle_request(request, socket_path)1286{1287if !ws.is_null() {1288// SAFETY: just checked that `ws` is not null.1289unsafe {1290*ws = match balloon_ws.try_into() {1291Ok(result) => result,1292Err(_) => return false,1293};1294}1295}12961297if !actual.is_null() {1298// SAFETY: just checked that `actual` is not null.1299unsafe {1300*actual = balloon_actual;1301}1302}1303true1304} else {1305false1306}1307} else {1308false1309}1310})1311.unwrap_or(false)1312}13131314/// Publically exposed version of RegisteredEvent enum, implemented as an1315/// integral newtype for FFI safety.1316#[repr(C)]1317#[derive(Copy, Clone, PartialEq, Eq)]1318pub struct RegisteredEventFfi(u32);13191320pub const REGISTERED_EVENT_VIRTIO_BALLOON_WS_REPORT: RegisteredEventFfi = RegisteredEventFfi(0);1321pub const REGISTERED_EVENT_VIRTIO_BALLOON_RESIZE: RegisteredEventFfi = RegisteredEventFfi(1);1322pub const REGISTERED_EVENT_VIRTIO_BALLOON_OOM_DEFLATION: RegisteredEventFfi = RegisteredEventFfi(2);13231324impl TryFrom<RegisteredEventFfi> for RegisteredEvent {1325type Error = &'static str;13261327fn try_from(value: RegisteredEventFfi) -> Result<Self, Self::Error> {1328match value.0 {13290 => Ok(RegisteredEvent::VirtioBalloonWsReport),13301 => Ok(RegisteredEvent::VirtioBalloonResize),13312 => Ok(RegisteredEvent::VirtioBalloonOOMDeflation),1332_ => Err("RegisteredEventFFi outside of known RegisteredEvent enum range"),1333}1334}1335}13361337/// Registers the connected process as a listener for `event`.1338///1339/// The function returns true on success or false if an error occurred.1340///1341/// # Safety1342///1343/// Function is unsafe due to raw pointer usage - `socket_path` and `listening_socket_path` should1344/// be a non-null pointers to C strings that are valid for reads and not modified for the duration1345/// of the call.1346#[no_mangle]1347pub unsafe extern "C" fn crosvm_client_register_events_listener(1348socket_path: *const c_char,1349listening_socket_path: *const c_char,1350event: RegisteredEventFfi,1351) -> bool {1352catch_unwind(|| {1353if let Some(socket_path) = validate_socket_path(socket_path) {1354if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {1355if let Ok(event) = event.try_into() {1356let request = VmRequest::RegisterListener {1357event,1358socket_addr: listening_socket_path.to_str().unwrap().to_string(),1359};1360vms_request(&request, socket_path).is_ok()1361} else {1362false1363}1364} else {1365false1366}1367} else {1368false1369}1370})1371.unwrap_or(false)1372}13731374/// Unegisters the connected process as a listener for `event`.1375///1376/// The function returns true on success or false if an error occurred.1377///1378/// # Safety1379///1380/// Function is unsafe due to raw pointer usage - `socket_path` and `listening_socket_path` should1381/// be a non-null pointers to C strings that are valid for reads and not modified for the duration1382/// of the call.1383#[no_mangle]1384pub unsafe extern "C" fn crosvm_client_unregister_events_listener(1385socket_path: *const c_char,1386listening_socket_path: *const c_char,1387event: RegisteredEventFfi,1388) -> bool {1389catch_unwind(|| {1390if let Some(socket_path) = validate_socket_path(socket_path) {1391if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {1392if let Ok(event) = event.try_into() {1393let request = VmRequest::UnregisterListener {1394event,1395socket_addr: listening_socket_path.to_str().unwrap().to_string(),1396};1397vms_request(&request, socket_path).is_ok()1398} else {1399false1400}1401} else {1402false1403}1404} else {1405false1406}1407})1408.unwrap_or(false)1409}14101411/// Unegisters the connected process as a listener for all events.1412///1413/// The function returns true on success or false if an error occurred.1414///1415/// # Safety1416///1417/// Function is unsafe due to raw pointer usage - `socket_path` and `listening_socket_path` should1418/// be a non-null pointers to C strings that are valid for reads and not modified for the duration1419/// of the call.1420#[no_mangle]1421pub unsafe extern "C" fn crosvm_client_unregister_listener(1422socket_path: *const c_char,1423listening_socket_path: *const c_char,1424) -> bool {1425catch_unwind(|| {1426if let Some(socket_path) = validate_socket_path(socket_path) {1427if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {1428let request = VmRequest::Unregister {1429socket_addr: listening_socket_path.to_str().unwrap().to_string(),1430};1431vms_request(&request, socket_path).is_ok()1432} else {1433false1434}1435} else {1436false1437}1438})1439.unwrap_or(false)1440}14411442/// Set Working Set Reporting config in guest.1443///1444/// The function returns true on success or false if an error occurred.1445///1446/// # Safety1447///1448/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1449/// string that is valid for reads and not modified for the duration of the call. `config` should1450/// be a pointer to a `BalloonWSRConfigFfi` valid for reads that is not modified for the duration1451/// of this call.1452#[no_mangle]1453pub unsafe extern "C" fn crosvm_client_balloon_wsr_config(1454socket_path: *const c_char,1455config: *const BalloonWSRConfigFfi,1456) -> bool {1457catch_unwind(|| {1458if let Some(socket_path) = validate_socket_path(socket_path) {1459if !config.is_null() {1460// SAFETY: just checked that `config` is not null.1461unsafe {1462if (*config).num_intervals > VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS as u8 {1463return false;1464}1465let mut actual_bins = vec![];1466for idx in 0..(*config).num_intervals {1467actual_bins.push((*config).intervals[idx as usize]);1468}1469let refresh_threshold = match u32::try_from((*config).refresh_threshold) {1470Ok(r_t) => r_t,1471Err(_) => return false,1472};1473let report_threshold = match u32::try_from((*config).report_threshold) {1474Ok(r_p) => r_p,1475Err(_) => return false,1476};1477let request =1478VmRequest::BalloonCommand(BalloonControlCommand::WorkingSetConfig {1479bins: actual_bins1480.iter()1481.map(|&b| u32::try_from(b).unwrap())1482.collect(),1483refresh_threshold,1484report_threshold,1485});1486vms_request(&request, socket_path).is_ok()1487}1488} else {1489false1490}1491} else {1492false1493}1494})1495.unwrap_or(false)1496}14971498/// Publicly exposed version enumeration of hypervisors, implemented as an1499/// integral newtype for FFI safety.1500#[repr(C)]1501#[derive(Copy, Clone, PartialEq, Eq)]1502pub struct HypervisorFfi(u32);15031504pub const HYPERVISOR_KVM: HypervisorFfi = HypervisorFfi(0);15051506impl TryFrom<&HypervisorKind> for HypervisorFfi {1507type Error = &'static str;15081509fn try_from(hypervisor: &HypervisorKind) -> Result<Self, Self::Error> {1510match hypervisor {1511HypervisorKind::Kvm => Ok(HYPERVISOR_KVM),1512_ => Err("unsupported hypervisor"),1513}1514}1515}15161517/// Hypervisor specific unique identifier of a VM.1518#[repr(C)]1519pub union HypervisorSpecificVmDescriptorFfi {1520// We use c_int instead of RawFd here because the std::os::fd crate is only available on unix1521// platforms.1522pub vm_fd: c_int,1523pub _reserved: u64,1524}15251526/// A unique identifier of a VM.1527#[repr(C)]1528pub struct VmDescriptorFfi {1529pub hypervisor: HypervisorFfi,1530pub descriptor: HypervisorSpecificVmDescriptorFfi,1531}15321533/// Get a descriptor representing a running VM.1534///1535/// The function returns true on success or false if an error occurred.1536///1537/// # Safety1538///1539/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1540/// string that is valid for reads and not modified for the duration of the call. `vm_desc_out`1541/// should be a pointer to a `VmDescriptorFfi` valid for writes that is not externally modified for1542/// the duration of this call.1543#[no_mangle]1544pub unsafe extern "C" fn crosvm_get_vm_descriptor(1545socket_path: *const c_char,1546vm_desc_out: *mut VmDescriptorFfi,1547) -> bool {1548catch_unwind(|| {1549let Some(socket_path) = validate_socket_path(socket_path) else {1550return false;1551};15521553if vm_desc_out.is_null() {1554return false;1555}15561557let resp = handle_request(&VmRequest::GetVmDescriptor, socket_path);1558if let Ok(VmResponse::VmDescriptor { hypervisor, vm_fd }) = resp {1559let Ok(hypervisor) = HypervisorFfi::try_from(&hypervisor) else {1560return false;1561};1562// SAFETY: just checked that `vm_desc_out` is not null.1563(*vm_desc_out).hypervisor = hypervisor;1564// On windows platforms RawDescriptor is actually a *mut c_void, hence cast to c_int1565// here.1566(*vm_desc_out).descriptor.vm_fd = vm_fd.into_raw_descriptor() as c_int;1567true1568} else {1569false1570}1571})1572.unwrap_or(false)1573}15741575/// Platform agnostic wrapper over a file descriptor.1576#[repr(C)]1577pub union FdWrapper {1578/// File descriptor on linux systems.1579pub linux_fd: c_int,1580/// File descriptor on windows systems.1581pub windows_fd: *mut c_void,1582}15831584/// Arguments structure for crosvm_add_memory.1585#[repr(C)]1586pub struct AddMemoryArgs {1587/// File descriptor representing memory (e.g. memfd or dma_buf_fd) shared with the VM.1588pub fd: FdWrapper,1589/// Offset.1590pub offset: u64,1591/// Start of the memory range in the guest VM that this memory will be mapped to.1592pub range_start: u64,1593/// End of the memory range in the guest VM that this memory will be mapped to.1594pub range_end: u64,1595/// Whether this memory is cache coherent or not.1596pub cache_coherent: bool,1597/// Padding for the future extensions.1598// TODO(ioffe): is one u64 enough?1599pub _reserved: u64,1600}16011602/// Registers memory represented by `memory_args` to the guest VM.1603///1604/// The function returns true on success or false if an error occurred. On success the1605/// `out_region_id` will contain the unique id representing the registered memory in guest.1606///1607/// # Safety1608///1609/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1610/// string that is valid for reads and not modified for the duration of the call. `memory_args`1611/// should be a pointer to `AddMemoryArgs` struct valid for read and not modified for the duration1612/// of this call. `out_region_id` should be a pointer to a `u64` valid for writes that is not1613/// externally modified for the duration of this call.1614/// This function takes the ownership of the `memory_args.fd` file descriptor.1615#[no_mangle]1616pub unsafe extern "C" fn crosvm_register_memory(1617socket_path: *const c_char,1618memory_args: *const AddMemoryArgs,1619out_region_id: *mut u64,1620) -> bool {1621catch_unwind(|| {1622// SAFETY: `memory_args.fd` is valid during the duration of this function.1623let fd = unsafe {1624#[cfg(not(target_os = "windows"))]1625{1626SafeDescriptor::from_raw_descriptor((*memory_args).fd.linux_fd)1627}1628#[cfg(target_os = "windows")]1629{1630SafeDescriptor::from_raw_descriptor((*memory_args).fd.windows_fd)1631}1632};16331634let Some(socket_path) = validate_socket_path(socket_path) else {1635return false;1636};16371638if out_region_id.is_null() {1639return false;1640}16411642let req = VmRequest::RegisterMemory {1643fd,1644offset: (*memory_args).offset,1645range_start: (*memory_args).range_start,1646range_end: (*memory_args).range_end,1647cache_coherent: (*memory_args).cache_coherent,1648};1649let resp = handle_request(&req, socket_path);1650if let Ok(VmResponse::RegisterMemory2 { region_id }) = resp {1651*out_region_id = region_id;1652true1653} else {1654false1655}1656})1657.unwrap_or(false)1658}16591660/// Unregisters memory represented by the `region_id` from the guest IPA space.1661///1662/// The function returns true on success or false if an error occurred.1663///1664/// # Safety1665///1666/// Function is unsafe due to raw pointer usage - `socket_path` should be a non-null pointer to a C1667/// string that is valid for reads and not modified for the duration of the call.1668#[no_mangle]1669pub unsafe extern "C" fn crosvm_unregister_memory(1670socket_path: *const c_char,1671region_id: u64,1672) -> bool {1673catch_unwind(|| {1674let Some(socket_path) = validate_socket_path(socket_path) else {1675return false;1676};16771678let req = VmRequest::UnregisterMemory { region_id };1679let resp = handle_request(&req, socket_path);1680matches!(resp, Ok(VmResponse::Ok))1681})1682.unwrap_or(false)1683}168416851686