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