zx/
vcpu.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{AsHandleRef, Guest, HandleBased, HandleRef, NullableHandle, Packet, Status, ok, sys};
6
7#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
8#[repr(transparent)]
9pub struct Vcpu(NullableHandle);
10impl_handle_based!(Vcpu);
11
12impl Vcpu {
13    /// Create a VCPU, for use with `guest` that begins execution at `entry`.
14    pub fn create(guest: &Guest, entry: usize) -> Result<Vcpu, Status> {
15        unsafe {
16            let mut vcpu_handle = 0;
17            ok(sys::zx_vcpu_create(guest.raw_handle(), 0, entry, &mut vcpu_handle))?;
18            Ok(Self::from(NullableHandle::from_raw(vcpu_handle)))
19        }
20    }
21
22    /// Enter a VCPU, causing it to resume execution.
23    pub fn enter(&self) -> Result<Packet, Status> {
24        let mut packet = Default::default();
25        ok(unsafe { sys::zx_vcpu_enter(self.raw_handle(), &mut packet) })?;
26        Ok(Packet(packet))
27    }
28
29    /// Kick a VCPU, causing it to stop execution.
30    pub fn kick(&self) -> Result<(), Status> {
31        ok(unsafe { sys::zx_vcpu_kick(self.raw_handle()) })
32    }
33
34    /// Raise an interrupt on a VCPU.
35    pub fn interrupt(&self, vector: u32) -> Result<(), Status> {
36        ok(unsafe { sys::zx_vcpu_interrupt(self.raw_handle(), vector) })
37    }
38
39    /// Read the state of a VCPU.
40    pub fn read_state(&self) -> Result<sys::zx_vcpu_state_t, Status> {
41        let mut state = sys::zx_vcpu_state_t::default();
42        let status = unsafe {
43            sys::zx_vcpu_read_state(
44                self.raw_handle(),
45                sys::ZX_VCPU_STATE,
46                std::ptr::from_mut(&mut state).cast::<u8>(),
47                std::mem::size_of_val(&state),
48            )
49        };
50        ok(status).map(|_| state)
51    }
52
53    /// Write the state of a VCPU.
54    pub fn write_state(&self, state: &sys::zx_vcpu_state_t) -> Result<(), Status> {
55        let status = unsafe {
56            sys::zx_vcpu_write_state(
57                self.raw_handle(),
58                sys::ZX_VCPU_STATE,
59                std::ptr::from_ref(state).cast::<u8>(),
60                std::mem::size_of_val(state),
61            )
62        };
63        ok(status)
64    }
65
66    /// Write the result of an IO trap to a VCPU.
67    pub fn write_io(&self, state: &sys::zx_vcpu_io_t) -> Result<(), Status> {
68        let status = unsafe {
69            sys::zx_vcpu_write_state(
70                self.raw_handle(),
71                sys::ZX_VCPU_IO,
72                std::ptr::from_ref(state).cast::<u8>(),
73                std::mem::size_of_val(state),
74            )
75        };
76        ok(status)
77    }
78}
79
80#[derive(Debug, Clone, Copy)]
81pub enum VcpuContents {
82    Interrupt { mask: u64, vector: u8 },
83    Startup { id: u64, entry: sys::zx_gpaddr_t },
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::Resource;
90    use fidl_fuchsia_kernel as fkernel;
91    use fuchsia_component::client::connect_to_protocol;
92
93    async fn get_hypervisor() -> Resource {
94        let resource = connect_to_protocol::<fkernel::HypervisorResourceMarker>()
95            .unwrap()
96            .get()
97            .await
98            .unwrap();
99        unsafe { Resource::from(NullableHandle::from_raw(resource.into_raw())) }
100    }
101
102    #[fuchsia::test]
103    async fn vcpu_create() {
104        let hypervisor = get_hypervisor().await;
105        let (guest, _vmar) = match Guest::create(&hypervisor) {
106            Err(Status::NOT_SUPPORTED) => {
107                println!("Hypervisor not supported");
108                return;
109            }
110            result => result.unwrap(),
111        };
112        let _vcpu = Vcpu::create(&guest, 0).unwrap();
113    }
114
115    #[fuchsia::test]
116    async fn vcpu_interrupt() {
117        let hypervisor = get_hypervisor().await;
118        let (guest, _vmar) = match Guest::create(&hypervisor) {
119            Err(Status::NOT_SUPPORTED) => {
120                println!("Hypervisor not supported");
121                return;
122            }
123            result => result.unwrap(),
124        };
125        let vcpu = Vcpu::create(&guest, 0).unwrap();
126
127        vcpu.interrupt(0).unwrap();
128    }
129
130    #[fuchsia::test]
131    async fn vcpu_read_write_state() {
132        let hypervisor = get_hypervisor().await;
133        let (guest, _vmar) = match Guest::create(&hypervisor) {
134            Err(Status::NOT_SUPPORTED) => {
135                println!("Hypervisor not supported");
136                return;
137            }
138            result => result.unwrap(),
139        };
140        let vcpu = Vcpu::create(&guest, 0).unwrap();
141
142        let state = vcpu.read_state().unwrap();
143        vcpu.write_state(&state).unwrap();
144    }
145}