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