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