blob: a73381a997caa9f7bf3d8ee6b10a099d3b37d1da [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use super::guest::Guest;
use fbl::UniquePtr;
use zx_status::Status;
use zx_types::{
zx_info_vcpu_t, zx_port_packet_t, zx_status_t, zx_vaddr_t, zx_vcpu_io_t, zx_vcpu_state_t,
};
unsafe extern "C" {
fn cpp_vcpu_create(
guest: *mut Guest,
entry: zx_vaddr_t,
vcpu_out: *mut *mut Vcpu,
) -> zx_status_t;
fn cpp_vcpu_destroy(vcpu: *mut Vcpu);
fn cpp_vcpu_enter(vcpu: *mut Vcpu, packet: *mut zx_port_packet_t) -> zx_status_t;
fn cpp_vcpu_kick(vcpu: *mut Vcpu);
fn cpp_vcpu_interrupt(vcpu: *mut Vcpu, vector: u32) -> zx_status_t;
fn cpp_vcpu_read_state(vcpu: *mut Vcpu, vcpu_state: *mut zx_vcpu_state_t) -> zx_status_t;
fn cpp_vcpu_write_state(vcpu: *mut Vcpu, vcpu_state: *const zx_vcpu_state_t) -> zx_status_t;
fn cpp_vcpu_write_io_state(vcpu: *mut Vcpu, io_state: *const zx_vcpu_io_t) -> zx_status_t;
fn cpp_vcpu_get_info(vcpu: *const Vcpu, info_out: *mut zx_info_vcpu_t);
}
fbl::impl_opaque_unique_facade!(
/// Facade for the C++ `Vcpu` class.
pub struct Vcpu,
cpp_vcpu_destroy,
);
impl Vcpu {
/// Returns a raw pointer to this `Vcpu` for FFI calls.
#[inline]
pub fn as_ffi(&self) -> *const Self {
self as *const Self
}
/// Returns a mutable raw pointer to this `Vcpu` for FFI calls.
#[inline]
pub fn as_ffi_mut(&self) -> *mut Self {
self as *const Self as *mut Self
}
/// Creates a new `Vcpu` within `guest`, which begins execution at `entry`.
pub fn create(guest: &Guest, entry: zx_vaddr_t) -> Result<UniquePtr<Self>, Status> {
let mut vcpu_raw = core::ptr::null_mut();
// SAFETY: `guest.as_ffi_mut()` is a valid `Guest` pointer for the lifetime of `guest`,
// and `vcpu_raw` is a valid out-pointer to receive the created `Vcpu*` on `ZX_OK`.
let status = unsafe { cpp_vcpu_create(guest.as_ffi_mut(), entry, &mut vcpu_raw) };
Status::ok(status)?;
// SAFETY: `cpp_vcpu_create` succeeded and returned a non-null owning `Vcpu*` released
// from `ktl::unique_ptr<Vcpu>`.
Ok(unsafe { UniquePtr::from_raw(vcpu_raw) })
}
/// Enters the guest, returning once the guest traps, faults, or is kicked.
pub fn enter(&self, packet: &mut zx_port_packet_t) -> Result<(), Status> {
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`, and
// `packet` is a valid out-pointer to an initialized `zx_port_packet_t`.
Status::ok(unsafe { cpp_vcpu_enter(self.as_ffi_mut(), packet) })
}
/// Kicks the VCPU out of the guest, causing a pending or future `enter` to return.
pub fn kick(&self) {
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`.
unsafe { cpp_vcpu_kick(self.as_ffi_mut()) }
}
/// Raises an interrupt on the VCPU.
pub fn interrupt(&self, vector: u32) -> Result<(), Status> {
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`.
Status::ok(unsafe { cpp_vcpu_interrupt(self.as_ffi_mut(), vector) })
}
/// Reads the VCPU's register state.
pub fn read_state(&self) -> Result<zx_vcpu_state_t, Status> {
let mut vcpu_state = zx_vcpu_state_t::default();
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`, and
// `&mut vcpu_state` is a valid out-pointer to an initialized `zx_vcpu_state_t`.
Status::ok(unsafe { cpp_vcpu_read_state(self.as_ffi_mut(), &mut vcpu_state) })?;
Ok(vcpu_state)
}
/// Writes the VCPU's register state.
pub fn write_state(&self, vcpu_state: &zx_vcpu_state_t) -> Result<(), Status> {
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`, and
// `vcpu_state` is a valid pointer that is only read from.
Status::ok(unsafe { cpp_vcpu_write_state(self.as_ffi_mut(), vcpu_state) })
}
/// Writes the VCPU's pending I/O state.
pub fn write_io_state(&self, io_state: &zx_vcpu_io_t) -> Result<(), Status> {
// SAFETY: `self.as_ffi_mut()` is a valid `Vcpu` pointer for the lifetime of `self`, and
// `io_state` is a valid pointer that is only read from.
Status::ok(unsafe { cpp_vcpu_write_io_state(self.as_ffi_mut(), io_state) })
}
/// Returns information about the VCPU.
pub fn get_info(&self) -> zx_info_vcpu_t {
let mut info = zx_info_vcpu_t::default();
// SAFETY: `self.as_ffi()` is a valid `Vcpu` pointer for the lifetime of `self`, and
// `&mut info` is a valid out-pointer that `cpp_vcpu_get_info` always initializes.
unsafe { cpp_vcpu_get_info(self.as_ffi(), &mut info) };
info
}
}