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