xref: /cloud-hypervisor/hypervisor/src/vm.rs (revision f7f2f25a574b1b2dba22c094fc8226d404157d15)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4 //
5 // Copyright © 2020, Microsoft Corporation
6 //
7 // Copyright 2018-2019 CrowdStrike, Inc.
8 //
9 //
10 
11 #[cfg(target_arch = "aarch64")]
12 use crate::aarch64::VcpuInit;
13 use crate::cpu::Vcpu;
14 use crate::device::Device;
15 #[cfg(feature = "tdx")]
16 use crate::x86_64::CpuId;
17 #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
18 use crate::ClockData;
19 #[cfg(feature = "kvm")]
20 use crate::CreateDevice;
21 #[cfg(feature = "mshv")]
22 use crate::HvState as VmState;
23 #[cfg(feature = "kvm")]
24 use crate::KvmVmState as VmState;
25 use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
26 #[cfg(feature = "kvm")]
27 use kvm_ioctls::Cap;
28 #[cfg(target_arch = "x86_64")]
29 use std::fs::File;
30 use std::sync::Arc;
31 use thiserror::Error;
32 use vmm_sys_util::eventfd::EventFd;
33 
34 ///
35 /// I/O events data matches (32 or 64 bits).
36 ///
37 #[derive(Debug)]
38 pub enum DataMatch {
39     DataMatch32(u32),
40     DataMatch64(u64),
41 }
42 
43 impl From<DataMatch> for u64 {
44     fn from(dm: DataMatch) -> u64 {
45         match dm {
46             DataMatch::DataMatch32(dm) => dm.into(),
47             DataMatch::DataMatch64(dm) => dm,
48         }
49     }
50 }
51 
52 #[derive(Error, Debug)]
53 ///
54 /// Enum for VM error
55 pub enum HypervisorVmError {
56     ///
57     /// Create Vcpu error
58     ///
59     #[error("Failed to create Vcpu: {0}")]
60     CreateVcpu(#[source] anyhow::Error),
61     ///
62     /// TSS address error
63     ///
64     #[error("Failed to set TSS address: {0}")]
65     SetTssAddress(#[source] anyhow::Error),
66     ///
67     /// Create interrupt controller error
68     ///
69     #[error("Failed to create interrupt controller: {0}")]
70     CreateIrq(#[source] anyhow::Error),
71     ///
72     /// Register interrupt event error
73     ///
74     #[error("Failed to register interrupt event: {0}")]
75     RegisterIrqFd(#[source] anyhow::Error),
76     ///
77     /// Un register interrupt event error
78     ///
79     #[error("Failed to unregister interrupt event: {0}")]
80     UnregisterIrqFd(#[source] anyhow::Error),
81     ///
82     /// Register IO event error
83     ///
84     #[error("Failed to register IO event: {0}")]
85     RegisterIoEvent(#[source] anyhow::Error),
86     ///
87     /// Unregister IO event error
88     ///
89     #[error("Failed to unregister IO event: {0}")]
90     UnregisterIoEvent(#[source] anyhow::Error),
91     ///
92     /// Set GSI routing error
93     ///
94     #[error("Failed to set GSI routing: {0}")]
95     SetGsiRouting(#[source] anyhow::Error),
96     ///
97     /// Create user memory error
98     ///
99     #[error("Failed to create user memory: {0}")]
100     CreateUserMemory(#[source] anyhow::Error),
101     ///
102     /// Remove user memory region error
103     ///
104     #[error("Failed to remove user memory: {0}")]
105     RemoveUserMemory(#[source] anyhow::Error),
106     ///
107     /// Create device error
108     ///
109     #[error("Failed to set GSI routing: {0}")]
110     CreateDevice(#[source] anyhow::Error),
111     ///
112     /// Get preferred target error
113     ///
114     #[error("Failed to get preferred target: {0}")]
115     GetPreferredTarget(#[source] anyhow::Error),
116     ///
117     /// Enable split Irq error
118     ///
119     #[error("Failed to enable split Irq: {0}")]
120     EnableSplitIrq(#[source] anyhow::Error),
121     ///
122     /// Enable SGX attribute error
123     ///
124     #[error("Failed to enable SGX attribute: {0}")]
125     EnableSgxAttribute(#[source] anyhow::Error),
126     ///
127     /// Get clock error
128     ///
129     #[error("Failed to get clock: {0}")]
130     GetClock(#[source] anyhow::Error),
131     ///
132     /// Set clock error
133     ///
134     #[error("Failed to set clock: {0}")]
135     SetClock(#[source] anyhow::Error),
136     ///
137     /// Create passthrough device
138     ///
139     #[error("Failed to create passthrough device: {0}")]
140     CreatePassthroughDevice(#[source] anyhow::Error),
141     /// Write to Guest memory
142     ///
143     #[error("Failed to write to guest memory: {0}")]
144     GuestMemWrite(#[source] anyhow::Error),
145     ///
146     /// Read Guest memory
147     ///
148     #[error("Failed to read guest memory: {0}")]
149     GuestMemRead(#[source] anyhow::Error),
150     ///
151     /// Read from MMIO Bus
152     ///
153     #[error("Failed to read from MMIO Bus: {0}")]
154     MmioBusRead(#[source] anyhow::Error),
155     ///
156     /// Write to MMIO Bus
157     ///
158     #[error("Failed to write to MMIO Bus: {0}")]
159     MmioBusWrite(#[source] anyhow::Error),
160     ///
161     /// Read from IO Bus
162     ///
163     #[error("Failed to read from IO Bus: {0}")]
164     IoBusRead(#[source] anyhow::Error),
165     ///
166     /// Write to IO Bus
167     ///
168     #[error("Failed to write to IO Bus: {0}")]
169     IoBusWrite(#[source] anyhow::Error),
170     ///
171     /// Start dirty log error
172     ///
173     #[error("Failed to get dirty log: {0}")]
174     StartDirtyLog(#[source] anyhow::Error),
175     ///
176     /// Stop dirty log error
177     ///
178     #[error("Failed to get dirty log: {0}")]
179     StopDirtyLog(#[source] anyhow::Error),
180     ///
181     /// Get dirty log error
182     ///
183     #[error("Failed to get dirty log: {0}")]
184     GetDirtyLog(#[source] anyhow::Error),
185     ///
186     /// Assert virtual interrupt error
187     ///
188     #[error("Failed to assert virtual Interrupt: {0}")]
189     AsserttVirtualInterrupt(#[source] anyhow::Error),
190 
191     #[cfg(feature = "tdx")]
192     ///
193     /// Error initializing TDX on the VM
194     ///
195     #[error("Failed to initialize TDX: {0}")]
196     InitializeTdx(#[source] std::io::Error),
197     #[cfg(feature = "tdx")]
198     ///
199     /// Error finalizing the TDX configuration on the VM
200     ///
201     #[error("Failed to finalize TDX: {0}")]
202     FinalizeTdx(#[source] std::io::Error),
203     #[cfg(feature = "tdx")]
204     ///
205     /// Error initializing the TDX memory region
206     ///
207     #[error("Failed to initialize memory region TDX: {0}")]
208     InitMemRegionTdx(#[source] std::io::Error),
209 }
210 ///
211 /// Result type for returning from a function
212 ///
213 pub type Result<T> = std::result::Result<T, HypervisorVmError>;
214 
215 ///
216 /// Trait to represent a Vm
217 ///
218 /// This crate provides a hypervisor-agnostic interfaces for Vm
219 ///
220 pub trait Vm: Send + Sync {
221     #[cfg(target_arch = "x86_64")]
222     /// Sets the address of the three-page region in the VM's address space.
223     fn set_tss_address(&self, offset: usize) -> Result<()>;
224     /// Creates an in-kernel interrupt controller.
225     fn create_irq_chip(&self) -> Result<()>;
226     /// Registers an event that will, when signaled, trigger the `gsi` IRQ.
227     fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
228     /// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
229     fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
230     /// Creates a new KVM vCPU file descriptor and maps the memory corresponding
231     fn create_vcpu(&self, id: u8, vmmops: Option<Arc<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>;
232     /// Registers an event to be signaled whenever a certain address is written to.
233     fn register_ioevent(
234         &self,
235         fd: &EventFd,
236         addr: &IoEventAddress,
237         datamatch: Option<DataMatch>,
238     ) -> Result<()>;
239     /// Unregister an event from a certain address it has been previously registered to.
240     fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
241     /// Sets the GSI routing table entries, overwriting any previously set
242     fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
243     /// Creates a memory region structure that can be used with {create/remove}_user_memory_region
244     fn make_user_memory_region(
245         &self,
246         slot: u32,
247         guest_phys_addr: u64,
248         memory_size: u64,
249         userspace_addr: u64,
250         readonly: bool,
251         log_dirty_pages: bool,
252     ) -> MemoryRegion;
253     /// Creates a guest physical memory slot.
254     fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
255     /// Removes a guest physical memory slot.
256     fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
257     #[cfg(feature = "kvm")]
258     /// Creates an emulated device in the kernel.
259     fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
260     /// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
261     #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
262     fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
263     /// Enable split Irq capability
264     #[cfg(target_arch = "x86_64")]
265     fn enable_split_irq(&self) -> Result<()>;
266     #[cfg(target_arch = "x86_64")]
267     fn enable_sgx_attribute(&self, file: File) -> Result<()>;
268     /// Retrieve guest clock.
269     #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
270     fn get_clock(&self) -> Result<ClockData>;
271     /// Set guest clock.
272     #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
273     fn set_clock(&self, data: &ClockData) -> Result<()>;
274     #[cfg(feature = "kvm")]
275     /// Checks if a particular `Cap` is available.
276     fn check_extension(&self, c: Cap) -> bool;
277     /// Create a device that is used for passthrough
278     fn create_passthrough_device(&self) -> Result<Arc<dyn Device>>;
279     /// Get the Vm state. Return VM specific data
280     fn state(&self) -> Result<VmState>;
281     /// Set the VM state
282     fn set_state(&self, state: VmState) -> Result<()>;
283     /// Start logging dirty pages
284     fn start_dirty_log(&self) -> Result<()>;
285     /// Stop logging dirty pages
286     fn stop_dirty_log(&self) -> Result<()>;
287     /// Get dirty pages bitmap
288     fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
289     #[cfg(feature = "tdx")]
290     /// Initalize TDX on this VM
291     fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
292     #[cfg(feature = "tdx")]
293     /// Finalize the configuration of TDX on this VM
294     fn tdx_finalize(&self) -> Result<()>;
295     #[cfg(feature = "tdx")]
296     /// Initalize a TDX memory region for this VM
297     fn tdx_init_memory_region(
298         &self,
299         host_address: u64,
300         guest_address: u64,
301         size: u64,
302         measure: bool,
303     ) -> Result<()>;
304 }
305 
306 pub trait VmmOps: Send + Sync {
307     fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
308     fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
309     fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
310     fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>;
311     #[cfg(target_arch = "x86_64")]
312     fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>;
313     #[cfg(target_arch = "x86_64")]
314     fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>;
315 }
316