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, Handle, HandleBased, HandleRef, Packet, Status, ok, sys};
6
7#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
8#[repr(transparent)]
9pub struct Vcpu(Handle);
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(Handle::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    use zx::HandleBased;
93
94    async fn get_hypervisor() -> Resource {
95        let resource = connect_to_protocol::<fkernel::HypervisorResourceMarker>()
96            .unwrap()
97            .get()
98            .await
99            .unwrap();
100        unsafe { Resource::from(Handle::from_raw(resource.into_raw())) }
101    }
102
103    #[fuchsia::test]
104    async fn vcpu_create() {
105        let hypervisor = get_hypervisor().await;
106        let (guest, _vmar) = match Guest::create(&hypervisor) {
107            Err(Status::NOT_SUPPORTED) => {
108                println!("Hypervisor not supported");
109                return;
110            }
111            result => result.unwrap(),
112        };
113        let _vcpu = Vcpu::create(&guest, 0).unwrap();
114    }
115
116    #[fuchsia::test]
117    async fn vcpu_interrupt() {
118        let hypervisor = get_hypervisor().await;
119        let (guest, _vmar) = match Guest::create(&hypervisor) {
120            Err(Status::NOT_SUPPORTED) => {
121                println!("Hypervisor not supported");
122                return;
123            }
124            result => result.unwrap(),
125        };
126        let vcpu = Vcpu::create(&guest, 0).unwrap();
127
128        vcpu.interrupt(0).unwrap();
129    }
130
131    #[fuchsia::test]
132    async fn vcpu_read_write_state() {
133        let hypervisor = get_hypervisor().await;
134        let (guest, _vmar) = match Guest::create(&hypervisor) {
135            Err(Status::NOT_SUPPORTED) => {
136                println!("Hypervisor not supported");
137                return;
138            }
139            result => result.unwrap(),
140        };
141        let vcpu = Vcpu::create(&guest, 0).unwrap();
142
143        let state = vcpu.read_state().unwrap();
144        vcpu.write_state(&state).unwrap();
145    }
146}