// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #![allow(non_camel_case_types)] //! This module implements the dynamically loaded client library API used by a crosvm plugin, //! defined in `crosvm.h`. It implements the client half of the plugin protocol, which is defined in //! the `protos::plugin` module. //! //! To implement the `crosvm.h` C API, each function and struct definition is repeated here, with //! concrete definitions for each struct. Most functions are thin shims to the underlying object //! oriented Rust implementation method. Most methods require a request over the crosvm connection, //! which is done by creating a `MainRequest` or `VcpuRequest` protobuf and sending it over the //! connection's socket. Then, that socket is read for a `MainResponse` or `VcpuResponse`, which is //! translated to the appropriate return type for the C API. use std::env; use std::fs::File; use std::io::{IoSlice, Read, Write}; use std::mem::{size_of, swap}; use std::os::raw::{c_int, c_void}; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::unix::net::UnixDatagram; use std::ptr::{self, null_mut}; use std::result; use std::slice; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use libc::{E2BIG, EINVAL, ENOENT, ENOTCONN, EPROTO}; use protobuf::{parse_from_bytes, Message, ProtobufEnum, RepeatedField}; use sys_util::ScmSocket; use kvm::dirty_log_bitmap_size; use kvm_sys::{ kvm_clock_data, kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_ioapic_state, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_pic_state, kvm_pit_state2, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs, }; use protos::plugin::*; #[cfg(feature = "stats")] mod stats; // Needs to be large enough to receive all the VCPU sockets. const MAX_DATAGRAM_FD: usize = 32; // Needs to be large enough for a sizable dirty log. const MAX_DATAGRAM_SIZE: usize = 0x40000; const CROSVM_IRQ_ROUTE_IRQCHIP: u32 = 0; const CROSVM_IRQ_ROUTE_MSI: u32 = 1; const CROSVM_VCPU_EVENT_KIND_INIT: u32 = 0; const CROSVM_VCPU_EVENT_KIND_IO_ACCESS: u32 = 1; const CROSVM_VCPU_EVENT_KIND_PAUSED: u32 = 2; const CROSVM_VCPU_EVENT_KIND_HYPERV_HCALL: u32 = 3; const CROSVM_VCPU_EVENT_KIND_HYPERV_SYNIC: u32 = 4; #[repr(C)] #[derive(Copy, Clone)] pub struct crosvm_net_config { tap_fd: c_int, host_ipv4_address: u32, netmask: u32, host_mac_address: [u8; 6], _reserved: [u8; 2], } #[repr(C)] #[derive(Copy, Clone)] pub struct anon_irqchip { irqchip: u32, pin: u32, } #[repr(C)] #[derive(Copy, Clone)] pub struct anon_msi { address: u64, data: u32, } #[repr(C)] pub union anon_route { irqchip: anon_irqchip, msi: anon_msi, reserved: [u8; 16], } #[repr(C)] pub struct crosvm_irq_route { irq_id: u32, kind: u32, route: anon_route, } const CROSVM_MAX_HINT_COUNT: u32 = 1; const CROSVM_MAX_HINT_DETAIL_COUNT: u32 = 32; const CROSVM_HINT_ON_WRITE: u16 = 1; #[repr(C)] pub struct crosvm_hint { hint_version: u32, reserved: u32, address_space: u32, address_flags: u16, details_count: u16, address: u64, details: *const crosvm_hint_detail, } #[repr(C)] pub struct crosvm_hint_detail { match_rax: bool, match_rbx: bool, match_rcx: bool, match_rdx: bool, reserved1: [u8; 4], rax: u64, rbx: u64, rcx: u64, rdx: u64, send_sregs: bool, send_debugregs: bool, reserved2: [u8; 6], } fn proto_error_to_int(e: protobuf::ProtobufError) -> c_int { match e { protobuf::ProtobufError::IoError(e) => e.raw_os_error().unwrap_or(EINVAL), _ => EINVAL, } } fn fd_cast(f: File) -> F { // Safe because we are transferring unique ownership. unsafe { F::from_raw_fd(f.into_raw_fd()) } } #[derive(Default)] struct IdAllocator(AtomicUsize); impl IdAllocator { fn alloc(&self) -> u32 { self.0.fetch_add(1, Ordering::Relaxed) as u32 } fn free(&self, id: u32) { self.0 .compare_and_swap(id as usize + 1, id as usize, Ordering::Relaxed); } } #[repr(u8)] #[derive(Debug, Clone, Copy)] pub enum Stat { IoEventFd, MemoryGetDirtyLog, IrqEventGetFd, IrqEventGetResampleFd, Connect, DestroyConnection, GetShutdownEventFd, CheckExtentsion, EnableVmCapability, EnableVcpuCapability, GetSupportedCpuid, GetEmulatedCpuid, GetHypervCpuid, GetMsrIndexList, NetGetConfig, ReserveRange, ReserveAsyncWriteRange, SetIrq, SetIrqRouting, GetPicState, SetPicState, GetIoapicState, SetIoapicState, GetPitState, SetPitState, GetClock, SetClock, SetIdentityMapAddr, PauseVcpus, Start, GetVcpu, VcpuWait, VcpuResume, VcpuGetRegs, VcpuSetRegs, VcpuGetSregs, VcpuSetSregs, GetFpu, SetFpu, GetDebugRegs, SetDebugRegs, GetXCRegs, SetXCRegs, VcpuGetMsrs, VcpuSetMsrs, VcpuSetCpuid, VcpuGetLapicState, VcpuSetLapicState, VcpuGetMpState, VcpuSetMpState, VcpuGetVcpuEvents, VcpuSetVcpuEvents, NewConnection, SetHypercallHint, Count, } #[cfg(feature = "stats")] fn record(a: Stat) -> stats::StatUpdater { unsafe { stats::STATS.record(a) } } #[cfg(not(feature = "stats"))] fn record(_a: Stat) -> u32 { 0 } #[cfg(feature = "stats")] fn printstats() { // Unsafe due to racy access - OK for stats if std::env::var("CROSVM_STATS").is_ok() { unsafe { stats::STATS.print(); } } } #[cfg(not(feature = "stats"))] fn printstats() {} pub struct crosvm { id_allocator: Arc, socket: UnixDatagram, request_buffer: Vec, response_buffer: Vec, vcpus: Arc>, } impl crosvm { fn from_connection(socket: UnixDatagram) -> result::Result { let mut crosvm = crosvm { id_allocator: Default::default(), socket, request_buffer: Vec::new(), response_buffer: vec![0; MAX_DATAGRAM_SIZE], vcpus: Default::default(), }; crosvm.load_all_vcpus()?; Ok(crosvm) } fn new( id_allocator: Arc, socket: UnixDatagram, vcpus: Arc>, ) -> crosvm { crosvm { id_allocator, socket, request_buffer: Vec::new(), response_buffer: vec![0; MAX_DATAGRAM_SIZE], vcpus, } } fn get_id_allocator(&self) -> &IdAllocator { &*self.id_allocator } fn main_transaction( &mut self, request: &MainRequest, fds: &[RawFd], ) -> result::Result<(MainResponse, Vec), c_int> { self.request_buffer.clear(); request .write_to_vec(&mut self.request_buffer) .map_err(proto_error_to_int)?; self.socket .send_with_fds(&[IoSlice::new(self.request_buffer.as_slice())], fds) .map_err(|e| -e.errno())?; let mut datagram_fds = [0; MAX_DATAGRAM_FD]; let (msg_size, fd_count) = self .socket .recv_with_fds(&mut self.response_buffer, &mut datagram_fds) .map_err(|e| -e.errno())?; // Safe because the first fd_count fds from recv_with_fds are owned by us and valid. let datagram_files = datagram_fds[..fd_count] .iter() .map(|&fd| unsafe { File::from_raw_fd(fd) }) .collect(); let response: MainResponse = parse_from_bytes(&self.response_buffer[..msg_size]).map_err(proto_error_to_int)?; if response.errno != 0 { return Err(response.errno); } Ok((response, datagram_files)) } fn try_clone(&mut self) -> result::Result { let mut r = MainRequest::new(); r.mut_new_connection(); let mut files = self.main_transaction(&r, &[])?.1; match files.pop() { Some(new_socket) => Ok(crosvm::new( self.id_allocator.clone(), fd_cast(new_socket), self.vcpus.clone(), )), None => Err(EPROTO), } } fn destroy(&mut self, id: u32) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_destroy().id = id; self.main_transaction(&r, &[])?; self.get_id_allocator().free(id); printstats(); Ok(()) } // Only call this at `from_connection` function. fn load_all_vcpus(&mut self) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_get_vcpus(); let (_, mut files) = self.main_transaction(&r, &[])?; if files.is_empty() || files.len() % 2 != 0 { return Err(EPROTO); } let mut vcpus = Vec::with_capacity(files.len() / 2); while files.len() > 1 { let write_pipe = files.remove(0); let read_pipe = files.remove(0); vcpus.push(crosvm_vcpu::new(fd_cast(read_pipe), fd_cast(write_pipe))); } // Only called once by the `from_connection` constructor, which makes a new unique // `self.vcpus`. let self_vcpus = Arc::get_mut(&mut self.vcpus).unwrap(); *self_vcpus = vcpus; Ok(()) } fn get_shutdown_eventfd(&mut self) -> result::Result { let mut r = MainRequest::new(); r.mut_get_shutdown_eventfd(); let (_, mut files) = self.main_transaction(&r, &[])?; match files.pop() { Some(f) => Ok(f), None => Err(EPROTO), } } fn check_extension(&mut self, extension: u32) -> result::Result { let mut r = MainRequest::new(); r.mut_check_extension().extension = extension; let (response, _) = self.main_transaction(&r, &[])?; if !response.has_check_extension() { return Err(EPROTO); } Ok(response.get_check_extension().has_extension) } fn get_supported_cpuid( &mut self, cpuid_entries: &mut [kvm_cpuid_entry2], cpuid_count: &mut usize, ) -> result::Result<(), c_int> { *cpuid_count = 0; let mut r = MainRequest::new(); r.mut_get_supported_cpuid(); let (response, _) = self.main_transaction(&r, &[])?; if !response.has_get_supported_cpuid() { return Err(EPROTO); } let supported_cpuids: &MainResponse_CpuidResponse = response.get_get_supported_cpuid(); *cpuid_count = supported_cpuids.get_entries().len(); if *cpuid_count > cpuid_entries.len() { return Err(E2BIG); } for (proto_entry, kvm_entry) in supported_cpuids .get_entries() .iter() .zip(cpuid_entries.iter_mut()) { *kvm_entry = cpuid_proto_to_kvm(proto_entry); } Ok(()) } fn get_emulated_cpuid( &mut self, cpuid_entries: &mut [kvm_cpuid_entry2], cpuid_count: &mut usize, ) -> result::Result<(), c_int> { *cpuid_count = 0; let mut r = MainRequest::new(); r.mut_get_emulated_cpuid(); let (response, _) = self.main_transaction(&r, &[])?; if !response.has_get_emulated_cpuid() { return Err(EPROTO); } let emulated_cpuids: &MainResponse_CpuidResponse = response.get_get_emulated_cpuid(); *cpuid_count = emulated_cpuids.get_entries().len(); if *cpuid_count > cpuid_entries.len() { return Err(E2BIG); } for (proto_entry, kvm_entry) in emulated_cpuids .get_entries() .iter() .zip(cpuid_entries.iter_mut()) { *kvm_entry = cpuid_proto_to_kvm(proto_entry); } Ok(()) } fn get_msr_index_list( &mut self, msr_indices: &mut [u32], msr_count: &mut usize, ) -> result::Result<(), c_int> { *msr_count = 0; let mut r = MainRequest::new(); r.mut_get_msr_index_list(); let (response, _) = self.main_transaction(&r, &[])?; if !response.has_get_msr_index_list() { return Err(EPROTO); } let msr_list: &MainResponse_MsrListResponse = response.get_get_msr_index_list(); *msr_count = msr_list.get_indices().len(); if *msr_count > msr_indices.len() { return Err(E2BIG); } for (proto_entry, kvm_entry) in msr_list.get_indices().iter().zip(msr_indices.iter_mut()) { *kvm_entry = *proto_entry; } Ok(()) } fn reserve_range( &mut self, space: u32, start: u64, length: u64, async_write: bool, ) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let reserve: &mut MainRequest_ReserveRange = r.mut_reserve_range(); reserve.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?; reserve.start = start; reserve.length = length; reserve.async_write = async_write; self.main_transaction(&r, &[])?; Ok(()) } fn set_irq(&mut self, irq_id: u32, active: bool) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let set_irq: &mut MainRequest_SetIrq = r.mut_set_irq(); set_irq.irq_id = irq_id; set_irq.active = active; self.main_transaction(&r, &[])?; Ok(()) } fn set_irq_routing(&mut self, routing: &[crosvm_irq_route]) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let set_irq_routing: &mut RepeatedField = r.mut_set_irq_routing().mut_routes(); for route in routing { let mut entry = MainRequest_SetIrqRouting_Route::new(); entry.irq_id = route.irq_id; match route.kind { CROSVM_IRQ_ROUTE_IRQCHIP => { let irqchip: &mut MainRequest_SetIrqRouting_Route_Irqchip; irqchip = entry.mut_irqchip(); // Safe because route.kind indicates which union field is valid. irqchip.irqchip = unsafe { route.route.irqchip }.irqchip; irqchip.pin = unsafe { route.route.irqchip }.pin; } CROSVM_IRQ_ROUTE_MSI => { let msi: &mut MainRequest_SetIrqRouting_Route_Msi = entry.mut_msi(); // Safe because route.kind indicates which union field is valid. msi.address = unsafe { route.route.msi }.address; msi.data = unsafe { route.route.msi }.data; } _ => return Err(EINVAL), } set_irq_routing.push(entry); } self.main_transaction(&r, &[])?; Ok(()) } fn set_hint( &mut self, space: u32, addr: u64, on_write: bool, hints: &[crosvm_hint_detail], ) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let req: &mut MainRequest_SetCallHint = r.mut_set_call_hint(); let set_hints: &mut RepeatedField = req.mut_hints(); for hint in hints { let mut entry = MainRequest_SetCallHint_RegHint::new(); entry.match_rax = hint.match_rax; entry.match_rbx = hint.match_rbx; entry.match_rcx = hint.match_rcx; entry.match_rdx = hint.match_rdx; entry.rax = hint.rax; entry.rbx = hint.rbx; entry.rcx = hint.rcx; entry.rdx = hint.rdx; entry.send_sregs = hint.send_sregs; entry.send_debugregs = hint.send_debugregs; set_hints.push(entry); } req.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?; req.address = addr; req.on_write = on_write; self.main_transaction(&r, &[])?; Ok(()) } fn get_state( &mut self, state_set: MainRequest_StateSet, out: &mut [u8], ) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_get_state().set = state_set; let (response, _) = self.main_transaction(&r, &[])?; if !response.has_get_state() { return Err(EPROTO); } let get_state: &MainResponse_GetState = response.get_get_state(); if get_state.state.len() != out.len() { return Err(EPROTO); } out.copy_from_slice(&get_state.state); Ok(()) } fn set_state( &mut self, state_set: MainRequest_StateSet, new_state: &[u8], ) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let set_state: &mut MainRequest_SetState = r.mut_set_state(); set_state.set = state_set; set_state.state = new_state.to_vec(); self.main_transaction(&r, &[])?; Ok(()) } fn set_identity_map_addr(&mut self, addr: u32) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_set_identity_map_addr().address = addr; self.main_transaction(&r, &[])?; Ok(()) } fn pause_vcpus(&mut self, cpu_mask: u64, user: *mut c_void) -> result::Result<(), c_int> { let mut r = MainRequest::new(); let pause_vcpus: &mut MainRequest_PauseVcpus = r.mut_pause_vcpus(); pause_vcpus.cpu_mask = cpu_mask; pause_vcpus.user = user as u64; self.main_transaction(&r, &[])?; Ok(()) } fn start(&mut self) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_start(); self.main_transaction(&r, &[])?; Ok(()) } fn get_vcpu(&mut self, cpu_id: u32) -> Result<*mut crosvm_vcpu, c_int> { if let Some(vcpu) = self.vcpus.get(cpu_id as usize) { Ok(vcpu as *const crosvm_vcpu as *mut crosvm_vcpu) } else { Err(ENOENT) } } fn get_net_config(&mut self) -> result::Result { let mut r = MainRequest::new(); r.mut_get_net_config(); let (response, mut files) = self.main_transaction(&r, &[])?; if !response.has_get_net_config() { return Err(EPROTO); } let config = response.get_get_net_config(); match files.pop() { Some(f) => { let mut net_config = crosvm_net_config { tap_fd: f.into_raw_fd(), host_ipv4_address: config.host_ipv4_address, netmask: config.netmask, host_mac_address: [0; 6], _reserved: [0; 2], }; let mac_addr = config.get_host_mac_address(); if mac_addr.len() != net_config.host_mac_address.len() { return Err(EPROTO); } net_config.host_mac_address.copy_from_slice(mac_addr); Ok(net_config) } None => Err(EPROTO), } } } /// This helper macro implements the C API's constructor/destructor for a given type. Because they /// all follow the same pattern and include lots of boilerplate unsafe code, it makes sense to write /// it once with this helper macro. macro_rules! impl_ctor_dtor { ( $t:ident, $ctor:ident ( $( $x:ident: $y:ty ),* ), $dtor:ident, ) => { #[allow(unused_unsafe)] #[no_mangle] pub unsafe extern fn $ctor(self_: *mut crosvm, $($x: $y,)* obj_ptr: *mut *mut $t) -> c_int { let self_ = &mut (*self_); match $t::create(self_, $($x,)*) { Ok(obj) => { *obj_ptr = Box::into_raw(Box::new(obj)); 0 } Err(e) => -e, } } #[no_mangle] pub unsafe extern fn $dtor(self_: *mut crosvm, obj_ptr: *mut *mut $t) -> c_int { let self_ = &mut (*self_); let obj = Box::from_raw(*obj_ptr); match self_.destroy(obj.id) { Ok(_) => { *obj_ptr = null_mut(); 0 } Err(e) => { Box::into_raw(obj); -e } } } } } pub struct crosvm_io_event { id: u32, evt: File, } impl crosvm_io_event { // Clippy: we use ptr::read_unaligned to read from pointers that may be // underaligned. Dereferencing such a pointer is always undefined behavior // in Rust. // // Lint can be unsuppressed once Clippy recognizes this pattern as correct. // https://github.com/rust-lang/rust-clippy/issues/2881 #[allow(clippy::cast_ptr_alignment)] unsafe fn create( crosvm: &mut crosvm, space: u32, addr: u64, length: u32, datamatch: *const u8, ) -> result::Result { let datamatch = match length { 0 => 0, 1 => ptr::read_unaligned(datamatch as *const u8) as u64, 2 => ptr::read_unaligned(datamatch as *const u16) as u64, 4 => ptr::read_unaligned(datamatch as *const u32) as u64, 8 => ptr::read_unaligned(datamatch as *const u64), _ => return Err(EINVAL), }; Self::safe_create(crosvm, space, addr, length, datamatch) } fn safe_create( crosvm: &mut crosvm, space: u32, addr: u64, length: u32, datamatch: u64, ) -> result::Result { let id = crosvm.get_id_allocator().alloc(); let mut r = MainRequest::new(); let create: &mut MainRequest_Create = r.mut_create(); create.id = id; let io_event: &mut MainRequest_Create_IoEvent = create.mut_io_event(); io_event.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?; io_event.address = addr; io_event.length = length; io_event.datamatch = datamatch; let ret = match crosvm.main_transaction(&r, &[]) { Ok((_, mut files)) => match files.pop() { Some(evt) => return Ok(crosvm_io_event { id, evt }), None => EPROTO, }, Err(e) => e, }; crosvm.get_id_allocator().free(id); Err(ret) } } impl_ctor_dtor!( crosvm_io_event, crosvm_create_io_event(space: u32, addr: u64, len: u32, datamatch: *const u8), crosvm_destroy_io_event, ); #[no_mangle] pub unsafe extern "C" fn crosvm_io_event_fd(this: *mut crosvm_io_event) -> c_int { let _u = record(Stat::IoEventFd); (*this).evt.as_raw_fd() } pub struct crosvm_memory { id: u32, length: u64, } impl crosvm_memory { fn create( crosvm: &mut crosvm, fd: c_int, offset: u64, length: u64, start: u64, read_only: bool, dirty_log: bool, ) -> result::Result { const PAGE_MASK: u64 = 0x0fff; if offset & PAGE_MASK != 0 || length & PAGE_MASK != 0 { return Err(EINVAL); } let id = crosvm.get_id_allocator().alloc(); let mut r = MainRequest::new(); let create: &mut MainRequest_Create = r.mut_create(); create.id = id; let memory: &mut MainRequest_Create_Memory = create.mut_memory(); memory.offset = offset; memory.start = start; memory.length = length; memory.read_only = read_only; memory.dirty_log = dirty_log; let ret = match crosvm.main_transaction(&r, &[fd]) { Ok(_) => return Ok(crosvm_memory { id, length }), Err(e) => e, }; crosvm.get_id_allocator().free(id); Err(ret) } fn get_dirty_log(&mut self, crosvm: &mut crosvm) -> result::Result, c_int> { let mut r = MainRequest::new(); r.mut_dirty_log().id = self.id; let (mut response, _) = crosvm.main_transaction(&r, &[])?; if !response.has_dirty_log() { return Err(EPROTO); } Ok(response.take_dirty_log().bitmap) } } impl_ctor_dtor!( crosvm_memory, crosvm_create_memory( fd: c_int, offset: u64, length: u64, start: u64, read_only: bool, dirty_log: bool ), crosvm_destroy_memory, ); #[no_mangle] pub unsafe extern "C" fn crosvm_memory_get_dirty_log( crosvm: *mut crosvm, this: *mut crosvm_memory, log: *mut u8, ) -> c_int { let _u = record(Stat::MemoryGetDirtyLog); let crosvm = &mut *crosvm; let this = &mut *this; let log_slice = slice::from_raw_parts_mut(log, dirty_log_bitmap_size(this.length as usize)); match this.get_dirty_log(crosvm) { Ok(bitmap) => { if bitmap.len() == log_slice.len() { log_slice.copy_from_slice(&bitmap); 0 } else { -EPROTO } } Err(e) => -e, } } pub struct crosvm_irq_event { id: u32, trigger_evt: File, resample_evt: File, } impl crosvm_irq_event { fn create(crosvm: &mut crosvm, irq_id: u32) -> result::Result { let id = crosvm.get_id_allocator().alloc(); let mut r = MainRequest::new(); let create: &mut MainRequest_Create = r.mut_create(); create.id = id; let irq_event: &mut MainRequest_Create_IrqEvent = create.mut_irq_event(); irq_event.irq_id = irq_id; irq_event.resample = true; let ret = match crosvm.main_transaction(&r, &[]) { Ok((_, mut files)) => { if files.len() >= 2 { let resample_evt = files.pop().unwrap(); let trigger_evt = files.pop().unwrap(); return Ok(crosvm_irq_event { id, trigger_evt, resample_evt, }); } EPROTO } Err(e) => e, }; crosvm.get_id_allocator().free(id); Err(ret) } } impl_ctor_dtor!( crosvm_irq_event, crosvm_create_irq_event(irq_id: u32), crosvm_destroy_irq_event, ); #[no_mangle] pub unsafe extern "C" fn crosvm_irq_event_get_fd(this: *mut crosvm_irq_event) -> c_int { let _u = record(Stat::IrqEventGetFd); (*this).trigger_evt.as_raw_fd() } #[no_mangle] pub unsafe extern "C" fn crosvm_irq_event_get_resample_fd(this: *mut crosvm_irq_event) -> c_int { let _u = record(Stat::IrqEventGetResampleFd); (*this).resample_evt.as_raw_fd() } #[allow(dead_code)] #[derive(Copy, Clone)] #[repr(C)] struct anon_io_access { address_space: u32, __reserved0: [u8; 4], address: u64, data: *mut u8, length: u32, is_write: u8, no_resume: u8, __reserved1: [u8; 2], } #[derive(Copy, Clone)] #[repr(C)] struct anon_hyperv_call { input: u64, result: *mut u8, params: [u64; 2], } #[derive(Copy, Clone)] #[repr(C)] struct anon_hyperv_synic { msr: u32, reserved: u32, control: u64, evt_page: u64, msg_page: u64, } #[repr(C)] union anon_vcpu_event { io_access: anon_io_access, user: *mut c_void, hyperv_call: anon_hyperv_call, hyperv_synic: anon_hyperv_synic, #[allow(dead_code)] __reserved: [u8; 64], } #[repr(C)] pub struct crosvm_vcpu_event { kind: u32, __reserved: [u8; 4], event: anon_vcpu_event, } // |get| tracks if the |cache| contains a cached value that can service get() // requests. A set() call will populate |cache| and |set| to true to record // that the next resume() should apply the state. We've got two choices on // what to do about |get| on a set(): 1) leave it as true, or 2) clear it and // have any call to get() first apply any pending set. Currently #2 is used // to favor correctness over performance (it gives KVM a chance to // modify/massage the values input to the set call). A plugin will rarely // (if ever) issue a get() after a set() on the same vcpu exit, so opting for // #1 is unlikely to provide a tangible performance gain. pub struct crosvm_vcpu_reg_cache { get: bool, set: bool, cache: Vec, } pub struct crosvm_vcpu { read_pipe: File, write_pipe: File, send_init: bool, request_buffer: Vec, response_buffer: Vec, response_base: usize, response_length: usize, resume_data: Vec, regs: crosvm_vcpu_reg_cache, sregs: crosvm_vcpu_reg_cache, debugregs: crosvm_vcpu_reg_cache, } fn read_varint32(data: &[u8]) -> (u32, usize) { let mut value: u32 = 0; let mut shift: u32 = 0; for (i, &b) in data.iter().enumerate() { if b < 0x80 { return match (b as u32).checked_shl(shift) { None => (0, 0), Some(b) => (value | b, i + 1), }; } match ((b as u32) & 0x7F).checked_shl(shift) { None => return (0, 0), Some(b) => value |= b, } shift += 7; } (0, 0) } impl crosvm_vcpu { fn new(read_pipe: File, write_pipe: File) -> crosvm_vcpu { crosvm_vcpu { read_pipe, write_pipe, send_init: true, request_buffer: Vec::new(), response_buffer: vec![0; MAX_DATAGRAM_SIZE], response_base: 0, response_length: 0, resume_data: Vec::new(), regs: crosvm_vcpu_reg_cache { get: false, set: false, cache: vec![], }, sregs: crosvm_vcpu_reg_cache { get: false, set: false, cache: vec![], }, debugregs: crosvm_vcpu_reg_cache { get: false, set: false, cache: vec![], }, } } fn vcpu_send(&mut self, request: &VcpuRequest) -> result::Result<(), c_int> { self.request_buffer.clear(); request .write_to_vec(&mut self.request_buffer) .map_err(proto_error_to_int)?; self.write_pipe .write(self.request_buffer.as_slice()) .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?; Ok(()) } fn vcpu_recv(&mut self) -> result::Result { if self.response_length == 0 { let msg_size = self .read_pipe .read(&mut self.response_buffer) .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?; self.response_base = 0; self.response_length = msg_size; } if self.response_length == 0 { return Err(EINVAL); } let (value, bytes) = read_varint32( &self.response_buffer[self.response_base..self.response_base + self.response_length], ); let total_size: usize = bytes + value as usize; if bytes == 0 || total_size > self.response_length { return Err(EINVAL); } let response: VcpuResponse = parse_from_bytes( &self.response_buffer[self.response_base + bytes..self.response_base + total_size], ) .map_err(proto_error_to_int)?; self.response_base += total_size; self.response_length -= total_size; if response.errno != 0 { return Err(response.errno); } Ok(response) } fn vcpu_transaction(&mut self, request: &VcpuRequest) -> result::Result { self.vcpu_send(request)?; let response: VcpuResponse = self.vcpu_recv()?; Ok(response) } fn wait(&mut self, event: &mut crosvm_vcpu_event) -> result::Result<(), c_int> { if self.send_init { self.send_init = false; let mut r = VcpuRequest::new(); r.mut_wait(); self.vcpu_send(&r)?; } let mut response: VcpuResponse = self.vcpu_recv()?; if !response.has_wait() { return Err(EPROTO); } let wait: &mut VcpuResponse_Wait = response.mut_wait(); if wait.has_init() { event.kind = CROSVM_VCPU_EVENT_KIND_INIT; self.regs.get = false; self.sregs.get = false; self.debugregs.get = false; Ok(()) } else if wait.has_io() { let mut io: VcpuResponse_Wait_Io = wait.take_io(); event.kind = CROSVM_VCPU_EVENT_KIND_IO_ACCESS; event.event.io_access = anon_io_access { address_space: io.space.value() as u32, __reserved0: Default::default(), address: io.address, data: io.data.as_mut_ptr(), length: io.data.len() as u32, is_write: io.is_write as u8, no_resume: io.no_resume as u8, __reserved1: Default::default(), }; self.resume_data = io.data; self.regs.get = !io.regs.is_empty(); if self.regs.get { swap(&mut self.regs.cache, &mut io.regs); } self.sregs.get = !io.sregs.is_empty(); if self.sregs.get { swap(&mut self.sregs.cache, &mut io.sregs); } self.debugregs.get = !io.debugregs.is_empty(); if self.debugregs.get { swap(&mut self.debugregs.cache, &mut io.debugregs); } Ok(()) } else if wait.has_user() { let user: &VcpuResponse_Wait_User = wait.get_user(); event.kind = CROSVM_VCPU_EVENT_KIND_PAUSED; event.event.user = user.user as *mut c_void; self.regs.get = false; self.sregs.get = false; self.debugregs.get = false; Ok(()) } else if wait.has_hyperv_call() { let hv: &VcpuResponse_Wait_HypervCall = wait.get_hyperv_call(); event.kind = CROSVM_VCPU_EVENT_KIND_HYPERV_HCALL; self.resume_data = vec![0; 8]; event.event.hyperv_call = anon_hyperv_call { input: hv.input, result: self.resume_data.as_mut_ptr(), params: [hv.params0, hv.params1], }; self.regs.get = false; self.sregs.get = false; self.debugregs.get = false; Ok(()) } else if wait.has_hyperv_synic() { let hv: &VcpuResponse_Wait_HypervSynic = wait.get_hyperv_synic(); event.kind = CROSVM_VCPU_EVENT_KIND_HYPERV_SYNIC; event.event.hyperv_synic = anon_hyperv_synic { msr: hv.msr, reserved: 0, control: hv.control, evt_page: hv.evt_page, msg_page: hv.msg_page, }; self.regs.get = false; self.sregs.get = false; self.debugregs.get = false; Ok(()) } else { Err(EPROTO) } } fn resume(&mut self) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); let resume: &mut VcpuRequest_Resume = r.mut_resume(); swap(&mut resume.data, &mut self.resume_data); if self.regs.set { swap(&mut resume.regs, &mut self.regs.cache); self.regs.set = false; } if self.sregs.set { swap(&mut resume.sregs, &mut self.sregs.cache); self.sregs.set = false; } if self.debugregs.set { swap(&mut resume.debugregs, &mut self.debugregs.cache); self.debugregs.set = false; } self.vcpu_send(&r)?; Ok(()) } fn get_state( &mut self, state_set: VcpuRequest_StateSet, out: &mut [u8], ) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); r.mut_get_state().set = state_set; let response = self.vcpu_transaction(&r)?; if !response.has_get_state() { return Err(EPROTO); } let get_state: &VcpuResponse_GetState = response.get_get_state(); if get_state.state.len() != out.len() { return Err(EPROTO); } out.copy_from_slice(&get_state.state); Ok(()) } fn set_state( &mut self, state_set: VcpuRequest_StateSet, new_state: &[u8], ) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); let set_state: &mut VcpuRequest_SetState = r.mut_set_state(); set_state.set = state_set; set_state.state = new_state.to_vec(); self.vcpu_transaction(&r)?; Ok(()) } fn set_state_from_cache( &mut self, state_set: VcpuRequest_StateSet, ) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); let set_state: &mut VcpuRequest_SetState = r.mut_set_state(); set_state.set = state_set; match state_set { VcpuRequest_StateSet::REGS => { swap(&mut set_state.state, &mut self.regs.cache); self.regs.set = false; } VcpuRequest_StateSet::SREGS => { swap(&mut set_state.state, &mut self.sregs.cache); self.sregs.set = false; } VcpuRequest_StateSet::DEBUGREGS => { swap(&mut set_state.state, &mut self.debugregs.cache); self.debugregs.set = false; } _ => return Err(EINVAL), } self.vcpu_transaction(&r)?; Ok(()) } fn get_hyperv_cpuid( &mut self, cpuid_entries: &mut [kvm_cpuid_entry2], cpuid_count: &mut usize, ) -> result::Result<(), c_int> { *cpuid_count = 0; let mut r = VcpuRequest::new(); r.mut_get_hyperv_cpuid(); let response = self.vcpu_transaction(&r)?; if !response.has_get_hyperv_cpuid() { return Err(EPROTO); } let hyperv_cpuids: &VcpuResponse_CpuidResponse = response.get_get_hyperv_cpuid(); *cpuid_count = hyperv_cpuids.get_entries().len(); if *cpuid_count > cpuid_entries.len() { return Err(E2BIG); } for (proto_entry, kvm_entry) in hyperv_cpuids .get_entries() .iter() .zip(cpuid_entries.iter_mut()) { *kvm_entry = cpuid_proto_to_kvm(proto_entry); } Ok(()) } fn get_msrs( &mut self, msr_entries: &mut [kvm_msr_entry], msr_count: &mut usize, ) -> result::Result<(), c_int> { *msr_count = 0; let mut r = VcpuRequest::new(); let entry_indices: &mut Vec = r.mut_get_msrs().mut_entry_indices(); for entry in msr_entries.iter() { entry_indices.push(entry.index); } let response = self.vcpu_transaction(&r)?; if !response.has_get_msrs() { return Err(EPROTO); } let get_msrs: &VcpuResponse_GetMsrs = response.get_get_msrs(); *msr_count = get_msrs.get_entry_data().len(); if *msr_count > msr_entries.len() { return Err(E2BIG); } for (&msr_data, msr_entry) in get_msrs.get_entry_data().iter().zip(msr_entries) { msr_entry.data = msr_data; } Ok(()) } fn set_msrs(&mut self, msr_entries: &[kvm_msr_entry]) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); let set_msrs_entries: &mut RepeatedField = r.mut_set_msrs().mut_entries(); for msr_entry in msr_entries { let mut entry = VcpuRequest_MsrEntry::new(); entry.index = msr_entry.index; entry.data = msr_entry.data; set_msrs_entries.push(entry); } self.vcpu_transaction(&r)?; Ok(()) } fn set_cpuid(&mut self, cpuid_entries: &[kvm_cpuid_entry2]) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); let set_cpuid_entries: &mut RepeatedField = r.mut_set_cpuid().mut_entries(); for cpuid_entry in cpuid_entries { set_cpuid_entries.push(cpuid_kvm_to_proto(cpuid_entry)); } self.vcpu_transaction(&r)?; Ok(()) } fn enable_capability(&mut self, capability: u32) -> result::Result<(), c_int> { let mut r = VcpuRequest::new(); r.mut_enable_capability().capability = capability; self.vcpu_transaction(&r)?; Ok(()) } } // crosvm API signals success as 0 and errors as negative values // derived from `errno`. fn to_crosvm_rc(r: result::Result) -> c_int { match r { Ok(_) => 0, Err(e) => -e, } } #[no_mangle] pub unsafe extern "C" fn crosvm_connect(out: *mut *mut crosvm) -> c_int { let _u = record(Stat::Connect); let socket_name = match env::var("CROSVM_SOCKET") { Ok(v) => v, _ => return -ENOTCONN, }; let socket = match socket_name.parse() { Ok(v) if v < 0 => return -EINVAL, Ok(v) => v, _ => return -EINVAL, }; let socket = UnixDatagram::from_raw_fd(socket); let crosvm = match crosvm::from_connection(socket) { Ok(c) => c, Err(e) => return -e, }; *out = Box::into_raw(Box::new(crosvm)); 0 } #[no_mangle] pub unsafe extern "C" fn crosvm_new_connection(self_: *mut crosvm, out: *mut *mut crosvm) -> c_int { let _u = record(Stat::NewConnection); let self_ = &mut (*self_); match self_.try_clone() { Ok(cloned) => { *out = Box::into_raw(Box::new(cloned)); 0 } Err(e) => -e, } } #[no_mangle] pub unsafe extern "C" fn crosvm_destroy_connection(self_: *mut *mut crosvm) -> c_int { let _u = record(Stat::DestroyConnection); Box::from_raw(*self_); *self_ = null_mut(); 0 } #[no_mangle] pub unsafe extern "C" fn crosvm_get_shutdown_eventfd(self_: *mut crosvm) -> c_int { let _u = record(Stat::GetShutdownEventFd); let self_ = &mut (*self_); match self_.get_shutdown_eventfd() { Ok(f) => f.into_raw_fd(), Err(e) => -e, } } #[no_mangle] pub unsafe extern "C" fn crosvm_check_extension( self_: *mut crosvm, extension: u32, has_extension: *mut bool, ) -> c_int { let _u = record(Stat::CheckExtentsion); let self_ = &mut (*self_); let ret = self_.check_extension(extension); if let Ok(supported) = ret { *has_extension = supported; } to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_enable_capability( _self_: *mut crosvm, _capability: u32, _flags: u32, _args: *const u64, ) -> c_int { let _u = record(Stat::EnableVmCapability); -EINVAL } #[no_mangle] pub unsafe extern "C" fn crosvm_get_supported_cpuid( this: *mut crosvm, entry_count: u32, cpuid_entries: *mut kvm_cpuid_entry2, out_count: *mut u32, ) -> c_int { let _u = record(Stat::GetSupportedCpuid); let this = &mut *this; let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize); let mut cpuid_count: usize = 0; let ret = this.get_supported_cpuid(cpuid_entries, &mut cpuid_count); *out_count = cpuid_count as u32; to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_emulated_cpuid( this: *mut crosvm, entry_count: u32, cpuid_entries: *mut kvm_cpuid_entry2, out_count: *mut u32, ) -> c_int { let _u = record(Stat::GetEmulatedCpuid); let this = &mut *this; let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize); let mut cpuid_count: usize = 0; let ret = this.get_emulated_cpuid(cpuid_entries, &mut cpuid_count); *out_count = cpuid_count as u32; to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_msr_index_list( this: *mut crosvm, entry_count: u32, msr_indices: *mut u32, out_count: *mut u32, ) -> c_int { let _u = record(Stat::GetMsrIndexList); let this = &mut *this; let msr_indices = from_raw_parts_mut(msr_indices, entry_count as usize); let mut msr_count: usize = 0; let ret = this.get_msr_index_list(msr_indices, &mut msr_count); *out_count = msr_count as u32; to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_net_get_config( self_: *mut crosvm, config: *mut crosvm_net_config, ) -> c_int { let _u = record(Stat::NetGetConfig); let self_ = &mut (*self_); let ret = self_.get_net_config(); if let Ok(c) = ret { *config = c; } to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_reserve_range( self_: *mut crosvm, space: u32, start: u64, length: u64, ) -> c_int { let _u = record(Stat::ReserveRange); let self_ = &mut (*self_); let ret = self_.reserve_range(space, start, length, false); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_reserve_async_write_range( self_: *mut crosvm, space: u32, start: u64, length: u64, ) -> c_int { let _u = record(Stat::ReserveAsyncWriteRange); let self_ = &mut (*self_); let ret = self_.reserve_range(space, start, length, true); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_irq(self_: *mut crosvm, irq_id: u32, active: bool) -> c_int { let _u = record(Stat::SetIrq); let self_ = &mut (*self_); let ret = self_.set_irq(irq_id, active); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_irq_routing( self_: *mut crosvm, route_count: u32, routes: *const crosvm_irq_route, ) -> c_int { let _u = record(Stat::SetIrqRouting); let self_ = &mut (*self_); let ret = self_.set_irq_routing(slice::from_raw_parts(routes, route_count as usize)); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_hypercall_hint( self_: *mut crosvm, hints_count: u32, hints: *const crosvm_hint, ) -> c_int { let _u = record(Stat::SetHypercallHint); let self_ = &mut (*self_); if hints_count < 1 { let ret = self_.set_hint(0, 0, false, &[]); return to_crosvm_rc(ret); } if hints_count > CROSVM_MAX_HINT_COUNT { return -EINVAL; } let hints = slice::from_raw_parts(hints, hints_count as usize); let hint = &hints[0]; if hint.hint_version != 0 || hint.reserved != 0 || hint.address == 0 || (hint.address_flags != 0 && hint.address_flags != CROSVM_HINT_ON_WRITE) || hint.details_count > CROSVM_MAX_HINT_DETAIL_COUNT as u16 { return -EINVAL; } let ret = self_.set_hint( hint.address_space, hint.address, hint.address_flags == CROSVM_HINT_ON_WRITE, slice::from_raw_parts(hint.details, hint.details_count as usize), ); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_pic_state( this: *mut crosvm, primary: bool, state: *mut kvm_pic_state, ) -> c_int { let _u = record(Stat::GetPicState); let this = &mut *this; let state_set = if primary { MainRequest_StateSet::PIC0 } else { MainRequest_StateSet::PIC1 }; let state = from_raw_parts_mut(state as *mut u8, size_of::()); let ret = this.get_state(state_set, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_pic_state( this: *mut crosvm, primary: bool, state: *mut kvm_pic_state, ) -> c_int { let _u = record(Stat::SetPicState); let this = &mut *this; let state_set = if primary { MainRequest_StateSet::PIC0 } else { MainRequest_StateSet::PIC1 }; let state = from_raw_parts(state as *mut u8, size_of::()); let ret = this.set_state(state_set, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_ioapic_state( this: *mut crosvm, state: *mut kvm_ioapic_state, ) -> c_int { let _u = record(Stat::GetIoapicState); let this = &mut *this; let state = from_raw_parts_mut(state as *mut u8, size_of::()); let ret = this.get_state(MainRequest_StateSet::IOAPIC, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_ioapic_state( this: *mut crosvm, state: *const kvm_ioapic_state, ) -> c_int { let _u = record(Stat::SetIoapicState); let this = &mut *this; let state = from_raw_parts(state as *mut u8, size_of::()); let ret = this.set_state(MainRequest_StateSet::IOAPIC, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_pit_state( this: *mut crosvm, state: *mut kvm_pit_state2, ) -> c_int { let _u = record(Stat::GetPitState); let this = &mut *this; let state = from_raw_parts_mut(state as *mut u8, size_of::()); let ret = this.get_state(MainRequest_StateSet::PIT, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_pit_state( this: *mut crosvm, state: *const kvm_pit_state2, ) -> c_int { let _u = record(Stat::SetPitState); let this = &mut *this; let state = from_raw_parts(state as *mut u8, size_of::()); let ret = this.set_state(MainRequest_StateSet::PIT, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_clock( this: *mut crosvm, clock_data: *mut kvm_clock_data, ) -> c_int { let _u = record(Stat::GetClock); let this = &mut *this; let state = from_raw_parts_mut(clock_data as *mut u8, size_of::()); let ret = this.get_state(MainRequest_StateSet::CLOCK, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_clock( this: *mut crosvm, clock_data: *const kvm_clock_data, ) -> c_int { let _u = record(Stat::SetClock); let this = &mut *this; let state = from_raw_parts(clock_data as *mut u8, size_of::()); let ret = this.set_state(MainRequest_StateSet::CLOCK, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_set_identity_map_addr(self_: *mut crosvm, addr: u32) -> c_int { let _u = record(Stat::SetIdentityMapAddr); let self_ = &mut (*self_); let ret = self_.set_identity_map_addr(addr); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_pause_vcpus( self_: *mut crosvm, cpu_mask: u64, user: *mut c_void, ) -> c_int { let _u = record(Stat::PauseVcpus); let self_ = &mut (*self_); let ret = self_.pause_vcpus(cpu_mask, user); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_start(self_: *mut crosvm) -> c_int { let _u = record(Stat::Start); let self_ = &mut (*self_); let ret = self_.start(); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_vcpu( self_: *mut crosvm, cpu_id: u32, out: *mut *mut crosvm_vcpu, ) -> c_int { let _u = record(Stat::GetVcpu); let self_ = &mut (*self_); let ret = self_.get_vcpu(cpu_id); if let Ok(vcpu) = ret { *out = vcpu; } to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_wait( this: *mut crosvm_vcpu, event: *mut crosvm_vcpu_event, ) -> c_int { let _u = record(Stat::VcpuWait); let this = &mut *this; let event = &mut *event; let ret = this.wait(event); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_resume(this: *mut crosvm_vcpu) -> c_int { let _u = record(Stat::VcpuResume); let this = &mut *this; let ret = this.resume(); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_regs( this: *mut crosvm_vcpu, regs: *mut kvm_regs, ) -> c_int { let _u = record(Stat::VcpuGetRegs); let this = &mut *this; if this.regs.set { if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::REGS) { return -e; } } let regs = from_raw_parts_mut(regs as *mut u8, size_of::()); if this.regs.get { regs.copy_from_slice(&this.regs.cache); 0 } else { let ret = this.get_state(VcpuRequest_StateSet::REGS, regs); to_crosvm_rc(ret) } } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_regs( this: *mut crosvm_vcpu, regs: *const kvm_regs, ) -> c_int { let _u = record(Stat::VcpuSetRegs); let this = &mut *this; this.regs.get = false; let regs = from_raw_parts(regs as *mut u8, size_of::()); this.regs.set = true; this.regs.cache = regs.to_vec(); 0 } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_sregs( this: *mut crosvm_vcpu, sregs: *mut kvm_sregs, ) -> c_int { let _u = record(Stat::VcpuGetSregs); let this = &mut *this; if this.sregs.set { if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::SREGS) { return -e; } } let sregs = from_raw_parts_mut(sregs as *mut u8, size_of::()); if this.sregs.get { sregs.copy_from_slice(&this.sregs.cache); 0 } else { let ret = this.get_state(VcpuRequest_StateSet::SREGS, sregs); to_crosvm_rc(ret) } } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_sregs( this: *mut crosvm_vcpu, sregs: *const kvm_sregs, ) -> c_int { let _u = record(Stat::VcpuSetSregs); let this = &mut *this; this.sregs.get = false; let sregs = from_raw_parts(sregs as *mut u8, size_of::()); this.sregs.set = true; this.sregs.cache = sregs.to_vec(); 0 } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_fpu(this: *mut crosvm_vcpu, fpu: *mut kvm_fpu) -> c_int { let _u = record(Stat::GetFpu); let this = &mut *this; let fpu = from_raw_parts_mut(fpu as *mut u8, size_of::()); let ret = this.get_state(VcpuRequest_StateSet::FPU, fpu); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_fpu(this: *mut crosvm_vcpu, fpu: *const kvm_fpu) -> c_int { let _u = record(Stat::SetFpu); let this = &mut *this; let fpu = from_raw_parts(fpu as *mut u8, size_of::()); let ret = this.set_state(VcpuRequest_StateSet::FPU, fpu); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_debugregs( this: *mut crosvm_vcpu, dregs: *mut kvm_debugregs, ) -> c_int { let _u = record(Stat::GetDebugRegs); let this = &mut *this; if this.debugregs.set { if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::DEBUGREGS) { return -e; } } let dregs = from_raw_parts_mut(dregs as *mut u8, size_of::()); if this.debugregs.get { dregs.copy_from_slice(&this.debugregs.cache); 0 } else { let ret = this.get_state(VcpuRequest_StateSet::DEBUGREGS, dregs); to_crosvm_rc(ret) } } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_debugregs( this: *mut crosvm_vcpu, dregs: *const kvm_debugregs, ) -> c_int { let _u = record(Stat::SetDebugRegs); let this = &mut *this; this.debugregs.get = false; let dregs = from_raw_parts(dregs as *mut u8, size_of::()); this.debugregs.set = true; this.debugregs.cache = dregs.to_vec(); 0 } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_xcrs( this: *mut crosvm_vcpu, xcrs: *mut kvm_xcrs, ) -> c_int { let _u = record(Stat::GetXCRegs); let this = &mut *this; let xcrs = from_raw_parts_mut(xcrs as *mut u8, size_of::()); let ret = this.get_state(VcpuRequest_StateSet::XCREGS, xcrs); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_xcrs( this: *mut crosvm_vcpu, xcrs: *const kvm_xcrs, ) -> c_int { let _u = record(Stat::SetXCRegs); let this = &mut *this; let xcrs = from_raw_parts(xcrs as *mut u8, size_of::()); let ret = this.set_state(VcpuRequest_StateSet::XCREGS, xcrs); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_get_hyperv_cpuid( this: *mut crosvm_vcpu, entry_count: u32, cpuid_entries: *mut kvm_cpuid_entry2, out_count: *mut u32, ) -> c_int { let _u = record(Stat::GetHypervCpuid); let this = &mut *this; let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize); let mut cpuid_count: usize = 0; let ret = this.get_hyperv_cpuid(cpuid_entries, &mut cpuid_count); *out_count = cpuid_count as u32; to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_msrs( this: *mut crosvm_vcpu, msr_count: u32, msr_entries: *mut kvm_msr_entry, out_count: *mut u32, ) -> c_int { let _u = record(Stat::VcpuGetMsrs); let this = &mut *this; let msr_entries = from_raw_parts_mut(msr_entries, msr_count as usize); let mut count: usize = 0; let ret = this.get_msrs(msr_entries, &mut count); *out_count = count as u32; to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_msrs( this: *mut crosvm_vcpu, msr_count: u32, msr_entries: *const kvm_msr_entry, ) -> c_int { let _u = record(Stat::VcpuSetMsrs); let this = &mut *this; let msr_entries = from_raw_parts(msr_entries, msr_count as usize); let ret = this.set_msrs(msr_entries); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_cpuid( this: *mut crosvm_vcpu, cpuid_count: u32, cpuid_entries: *const kvm_cpuid_entry2, ) -> c_int { let _u = record(Stat::VcpuSetCpuid); let this = &mut *this; let cpuid_entries = from_raw_parts(cpuid_entries, cpuid_count as usize); let ret = this.set_cpuid(cpuid_entries); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_enable_capability( this: *mut crosvm_vcpu, capability: u32, flags: u32, args: *const u64, ) -> c_int { let _u = record(Stat::EnableVcpuCapability); let this = &mut *this; let args = slice::from_raw_parts(args, 4); if flags != 0 || args.iter().any(|v| *v != 0) { return -EINVAL; } let ret = this.enable_capability(capability); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_lapic_state( this: *mut crosvm_vcpu, state: *mut kvm_lapic_state, ) -> c_int { let _u = record(Stat::VcpuGetLapicState); let this = &mut *this; let state = from_raw_parts_mut(state as *mut u8, size_of::()); let ret = this.get_state(VcpuRequest_StateSet::LAPIC, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_lapic_state( this: *mut crosvm_vcpu, state: *const kvm_lapic_state, ) -> c_int { let _u = record(Stat::VcpuSetLapicState); let this = &mut *this; let state = from_raw_parts(state as *mut u8, size_of::()); let ret = this.set_state(VcpuRequest_StateSet::LAPIC, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_mp_state( this: *mut crosvm_vcpu, state: *mut kvm_mp_state, ) -> c_int { let _u = record(Stat::VcpuGetMpState); let this = &mut *this; let state = from_raw_parts_mut(state as *mut u8, size_of::()); let ret = this.get_state(VcpuRequest_StateSet::MP, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_mp_state( this: *mut crosvm_vcpu, state: *const kvm_mp_state, ) -> c_int { let _u = record(Stat::VcpuSetMpState); let this = &mut *this; let state = from_raw_parts(state as *mut u8, size_of::()); let ret = this.set_state(VcpuRequest_StateSet::MP, state); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_get_vcpu_events( this: *mut crosvm_vcpu, events: *mut kvm_vcpu_events, ) -> c_int { let _u = record(Stat::VcpuGetVcpuEvents); let this = &mut *this; let events = from_raw_parts_mut(events as *mut u8, size_of::()); let ret = this.get_state(VcpuRequest_StateSet::EVENTS, events); to_crosvm_rc(ret) } #[no_mangle] pub unsafe extern "C" fn crosvm_vcpu_set_vcpu_events( this: *mut crosvm_vcpu, events: *const kvm_vcpu_events, ) -> c_int { let _u = record(Stat::VcpuSetVcpuEvents); let this = &mut *this; let events = from_raw_parts(events as *mut u8, size_of::()); let ret = this.set_state(VcpuRequest_StateSet::EVENTS, events); to_crosvm_rc(ret) }