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