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