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