xref: /cloud-hypervisor/hypervisor/src/vm.rs (revision 274f1aa2e738d579ffff9d4cfd7ed7c45293af31)
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 #[cfg(target_arch = "aarch64")]
14 use crate::arch::aarch64::gic::{Vgic, VgicConfig};
15 #[cfg(feature = "tdx")]
16 use crate::arch::x86::CpuIdEntry;
17 use crate::cpu::Vcpu;
18 #[cfg(target_arch = "x86_64")]
19 use crate::ClockData;
20 use crate::UserMemoryRegion;
21 use crate::{IoEventAddress, IrqRoutingEntry};
22 use std::any::Any;
23 #[cfg(target_arch = "x86_64")]
24 use std::fs::File;
25 use std::sync::Arc;
26 #[cfg(target_arch = "aarch64")]
27 use std::sync::Mutex;
28 use thiserror::Error;
29 use vmm_sys_util::eventfd::EventFd;
30 
31 ///
32 /// I/O events data matches (32 or 64 bits).
33 ///
34 #[derive(Debug)]
35 pub enum DataMatch {
36     DataMatch32(u32),
37     DataMatch64(u64),
38 }
39 
40 impl From<DataMatch> for u64 {
41     fn from(dm: DataMatch) -> u64 {
42         match dm {
43             DataMatch::DataMatch32(dm) => dm.into(),
44             DataMatch::DataMatch64(dm) => dm,
45         }
46     }
47 }
48 
49 #[derive(Error, Debug)]
50 ///
51 /// Enum for VM error
52 pub enum HypervisorVmError {
53     ///
54     /// Create Vcpu error
55     ///
56     #[error("Failed to create Vcpu: {0}")]
57     CreateVcpu(#[source] anyhow::Error),
58     ///
59     /// Identity map address error
60     ///
61     #[error("Failed to set identity map address: {0}")]
62     SetIdentityMapAddress(#[source] anyhow::Error),
63     ///
64     /// TSS address error
65     ///
66     #[error("Failed to set TSS address: {0}")]
67     SetTssAddress(#[source] anyhow::Error),
68     ///
69     /// Create interrupt controller error
70     ///
71     #[error("Failed to create interrupt controller: {0}")]
72     CreateIrq(#[source] anyhow::Error),
73     ///
74     /// Register interrupt event error
75     ///
76     #[error("Failed to register interrupt event: {0}")]
77     RegisterIrqFd(#[source] anyhow::Error),
78     ///
79     /// Un register interrupt event error
80     ///
81     #[error("Failed to unregister interrupt event: {0}")]
82     UnregisterIrqFd(#[source] anyhow::Error),
83     ///
84     /// Register IO event error
85     ///
86     #[error("Failed to register IO event: {0}")]
87     RegisterIoEvent(#[source] anyhow::Error),
88     ///
89     /// Unregister IO event error
90     ///
91     #[error("Failed to unregister IO event: {0}")]
92     UnregisterIoEvent(#[source] anyhow::Error),
93     ///
94     /// Set GSI routing error
95     ///
96     #[error("Failed to set GSI routing: {0}")]
97     SetGsiRouting(#[source] anyhow::Error),
98     ///
99     /// Create user memory error
100     ///
101     #[error("Failed to create user memory: {0}")]
102     CreateUserMemory(#[source] anyhow::Error),
103     ///
104     /// Remove user memory region error
105     ///
106     #[error("Failed to remove user memory: {0}")]
107     RemoveUserMemory(#[source] anyhow::Error),
108     ///
109     /// Create device error
110     ///
111     #[error("Failed to set GSI routing: {0}")]
112     CreateDevice(#[source] anyhow::Error),
113     ///
114     /// Get preferred target error
115     ///
116     #[error("Failed to get preferred target: {0}")]
117     GetPreferredTarget(#[source] anyhow::Error),
118     ///
119     /// Enable split Irq error
120     ///
121     #[error("Failed to enable split Irq: {0}")]
122     EnableSplitIrq(#[source] anyhow::Error),
123     ///
124     /// Enable SGX attribute error
125     ///
126     #[error("Failed to enable SGX attribute: {0}")]
127     EnableSgxAttribute(#[source] anyhow::Error),
128     ///
129     /// Get clock error
130     ///
131     #[error("Failed to get clock: {0}")]
132     GetClock(#[source] anyhow::Error),
133     ///
134     /// Set clock error
135     ///
136     #[error("Failed to set clock: {0}")]
137     SetClock(#[source] anyhow::Error),
138     ///
139     /// Create passthrough device
140     ///
141     #[error("Failed to create passthrough device: {0}")]
142     CreatePassthroughDevice(#[source] anyhow::Error),
143     /// Write to Guest memory
144     ///
145     #[error("Failed to write to guest memory: {0}")]
146     GuestMemWrite(#[source] anyhow::Error),
147     ///
148     /// Read Guest memory
149     ///
150     #[error("Failed to read guest memory: {0}")]
151     GuestMemRead(#[source] anyhow::Error),
152     ///
153     /// Read from MMIO Bus
154     ///
155     #[error("Failed to read from MMIO Bus: {0}")]
156     MmioBusRead(#[source] anyhow::Error),
157     ///
158     /// Write to MMIO Bus
159     ///
160     #[error("Failed to write to MMIO Bus: {0}")]
161     MmioBusWrite(#[source] anyhow::Error),
162     ///
163     /// Read from IO Bus
164     ///
165     #[error("Failed to read from IO Bus: {0}")]
166     IoBusRead(#[source] anyhow::Error),
167     ///
168     /// Write to IO Bus
169     ///
170     #[error("Failed to write to IO Bus: {0}")]
171     IoBusWrite(#[source] anyhow::Error),
172     ///
173     /// Start dirty log error
174     ///
175     #[error("Failed to get dirty log: {0}")]
176     StartDirtyLog(#[source] anyhow::Error),
177     ///
178     /// Stop dirty log error
179     ///
180     #[error("Failed to get dirty log: {0}")]
181     StopDirtyLog(#[source] anyhow::Error),
182     ///
183     /// Get dirty log error
184     ///
185     #[error("Failed to get dirty log: {0}")]
186     GetDirtyLog(#[source] anyhow::Error),
187     ///
188     /// Assert virtual interrupt error
189     ///
190     #[error("Failed to assert virtual Interrupt: {0}")]
191     AsserttVirtualInterrupt(#[source] anyhow::Error),
192 
193     #[cfg(feature = "sev_snp")]
194     ///
195     /// Error initializing SEV-SNP on the VM
196     ///
197     #[error("Failed to initialize SEV-SNP: {0}")]
198     InitializeSevSnp(#[source] std::io::Error),
199 
200     #[cfg(feature = "tdx")]
201     ///
202     /// Error initializing TDX on the VM
203     ///
204     #[error("Failed to initialize TDX: {0}")]
205     InitializeTdx(#[source] std::io::Error),
206     #[cfg(feature = "tdx")]
207     ///
208     /// Error finalizing the TDX configuration on the VM
209     ///
210     #[error("Failed to finalize TDX: {0}")]
211     FinalizeTdx(#[source] std::io::Error),
212     #[cfg(feature = "tdx")]
213     ///
214     /// Error initializing the TDX memory region
215     ///
216     #[error("Failed to initialize memory region TDX: {0}")]
217     InitMemRegionTdx(#[source] std::io::Error),
218     ///
219     /// Create Vgic error
220     ///
221     #[error("Failed to create Vgic: {0}")]
222     CreateVgic(#[source] anyhow::Error),
223 }
224 ///
225 /// Result type for returning from a function
226 ///
227 pub type Result<T> = std::result::Result<T, HypervisorVmError>;
228 
229 /// Configuration data for legacy interrupts.
230 ///
231 /// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
232 #[derive(Copy, Clone, Debug)]
233 pub struct LegacyIrqSourceConfig {
234     pub irqchip: u32,
235     pub pin: u32,
236 }
237 
238 /// Configuration data for MSI/MSI-X interrupts.
239 ///
240 /// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
241 #[derive(Copy, Clone, Debug, Default)]
242 pub struct MsiIrqSourceConfig {
243     /// High address to delivery message signaled interrupt.
244     pub high_addr: u32,
245     /// Low address to delivery message signaled interrupt.
246     pub low_addr: u32,
247     /// Data to write to delivery message signaled interrupt.
248     pub data: u32,
249     /// Unique ID of the device to delivery message signaled interrupt.
250     pub devid: u32,
251 }
252 
253 /// Configuration data for an interrupt source.
254 #[derive(Copy, Clone, Debug)]
255 pub enum InterruptSourceConfig {
256     /// Configuration data for Legacy interrupts.
257     LegacyIrq(LegacyIrqSourceConfig),
258     /// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
259     MsiIrq(MsiIrqSourceConfig),
260 }
261 
262 ///
263 /// Trait to represent a Vm
264 ///
265 /// This crate provides a hypervisor-agnostic interfaces for Vm
266 ///
267 pub trait Vm: Send + Sync + Any {
268     #[cfg(target_arch = "x86_64")]
269     /// Sets the address of the one-page region in the VM's address space.
270     fn set_identity_map_address(&self, address: u64) -> Result<()>;
271     #[cfg(target_arch = "x86_64")]
272     /// Sets the address of the three-page region in the VM's address space.
273     fn set_tss_address(&self, offset: usize) -> Result<()>;
274     /// Creates an in-kernel interrupt controller.
275     fn create_irq_chip(&self) -> Result<()>;
276     /// Registers an event that will, when signaled, trigger the `gsi` IRQ.
277     fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
278     /// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
279     fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
280     /// Creates a new KVM vCPU file descriptor and maps the memory corresponding
281     fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
282     #[cfg(target_arch = "aarch64")]
283     fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>;
284 
285     /// Registers an event to be signaled whenever a certain address is written to.
286     fn register_ioevent(
287         &self,
288         fd: &EventFd,
289         addr: &IoEventAddress,
290         datamatch: Option<DataMatch>,
291     ) -> Result<()>;
292     /// Unregister an event from a certain address it has been previously registered to.
293     fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>;
294     // Construct a routing entry
295     fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry;
296     /// Sets the GSI routing table entries, overwriting any previously set
297     fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>;
298     /// Creates a memory region structure that can be used with {create/remove}_user_memory_region
299     fn make_user_memory_region(
300         &self,
301         slot: u32,
302         guest_phys_addr: u64,
303         memory_size: u64,
304         userspace_addr: u64,
305         readonly: bool,
306         log_dirty_pages: bool,
307     ) -> UserMemoryRegion;
308     /// Creates a guest physical memory slot.
309     fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
310     /// Removes a guest physical memory slot.
311     fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
312     /// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
313     #[cfg(target_arch = "aarch64")]
314     fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
315     /// Enable split Irq capability
316     #[cfg(target_arch = "x86_64")]
317     fn enable_split_irq(&self) -> Result<()>;
318     #[cfg(target_arch = "x86_64")]
319     fn enable_sgx_attribute(&self, file: File) -> Result<()>;
320     /// Retrieve guest clock.
321     #[cfg(target_arch = "x86_64")]
322     fn get_clock(&self) -> Result<ClockData>;
323     /// Set guest clock.
324     #[cfg(target_arch = "x86_64")]
325     fn set_clock(&self, data: &ClockData) -> Result<()>;
326     /// Create a device that is used for passthrough
327     fn create_passthrough_device(&self) -> Result<vfio_ioctls::VfioDeviceFd>;
328     /// Start logging dirty pages
329     fn start_dirty_log(&self) -> Result<()>;
330     /// Stop logging dirty pages
331     fn stop_dirty_log(&self) -> Result<()>;
332     /// Get dirty pages bitmap
333     fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>;
334     #[cfg(feature = "sev_snp")]
335     /// Initialize SEV-SNP on this VM
336     fn sev_snp_init(&self) -> Result<()> {
337         unimplemented!()
338     }
339     #[cfg(feature = "tdx")]
340     /// Initialize TDX on this VM
341     fn tdx_init(&self, _cpuid: &[CpuIdEntry], _max_vcpus: u32) -> Result<()> {
342         unimplemented!()
343     }
344     #[cfg(feature = "tdx")]
345     /// Finalize the configuration of TDX on this VM
346     fn tdx_finalize(&self) -> Result<()> {
347         unimplemented!()
348     }
349     #[cfg(feature = "tdx")]
350     /// Initialize a TDX memory region for this VM
351     fn tdx_init_memory_region(
352         &self,
353         _host_address: u64,
354         _guest_address: u64,
355         _size: u64,
356         _measure: bool,
357     ) -> Result<()> {
358         unimplemented!()
359     }
360     /// Downcast to the underlying hypervisor VM type
361     fn as_any(&self) -> &dyn Any;
362 }
363 
364 pub trait VmOps: Send + Sync {
365     fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>;
366     fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>;
367     fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>;
368     fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>;
369     #[cfg(target_arch = "x86_64")]
370     fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>;
371     #[cfg(target_arch = "x86_64")]
372     fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>;
373 }
374