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 { 46 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: {0}")] 62 CreateVcpu(#[source] anyhow::Error), 63 /// 64 /// Identity map address error 65 /// 66 #[error("Failed to set identity map address: {0}")] 67 SetIdentityMapAddress(#[source] anyhow::Error), 68 /// 69 /// TSS address error 70 /// 71 #[error("Failed to set TSS address: {0}")] 72 SetTssAddress(#[source] anyhow::Error), 73 /// 74 /// Create interrupt controller error 75 /// 76 #[error("Failed to create interrupt controller: {0}")] 77 CreateIrq(#[source] anyhow::Error), 78 /// 79 /// Register interrupt event error 80 /// 81 #[error("Failed to register interrupt event: {0}")] 82 RegisterIrqFd(#[source] anyhow::Error), 83 /// 84 /// Un register interrupt event error 85 /// 86 #[error("Failed to unregister interrupt event: {0}")] 87 UnregisterIrqFd(#[source] anyhow::Error), 88 /// 89 /// Register IO event error 90 /// 91 #[error("Failed to register IO event: {0}")] 92 RegisterIoEvent(#[source] anyhow::Error), 93 /// 94 /// Unregister IO event error 95 /// 96 #[error("Failed to unregister IO event: {0}")] 97 UnregisterIoEvent(#[source] anyhow::Error), 98 /// 99 /// Set GSI routing error 100 /// 101 #[error("Failed to set GSI routing: {0}")] 102 SetGsiRouting(#[source] anyhow::Error), 103 /// 104 /// Create user memory error 105 /// 106 #[error("Failed to create user memory: {0}")] 107 CreateUserMemory(#[source] anyhow::Error), 108 /// 109 /// Remove user memory region error 110 /// 111 #[error("Failed to remove user memory: {0}")] 112 RemoveUserMemory(#[source] anyhow::Error), 113 /// 114 /// Create device error 115 /// 116 #[error("Failed to set GSI routing: {0}")] 117 CreateDevice(#[source] anyhow::Error), 118 /// 119 /// Get preferred target error 120 /// 121 #[error("Failed to get preferred target: {0}")] 122 GetPreferredTarget(#[source] anyhow::Error), 123 /// 124 /// Enable split Irq error 125 /// 126 #[error("Failed to enable split Irq: {0}")] 127 EnableSplitIrq(#[source] anyhow::Error), 128 /// 129 /// Enable SGX attribute error 130 /// 131 #[error("Failed to enable SGX attribute: {0}")] 132 EnableSgxAttribute(#[source] anyhow::Error), 133 /// 134 /// Get clock error 135 /// 136 #[error("Failed to get clock: {0}")] 137 GetClock(#[source] anyhow::Error), 138 /// 139 /// Set clock error 140 /// 141 #[error("Failed to set clock: {0}")] 142 SetClock(#[source] anyhow::Error), 143 /// 144 /// Create passthrough device 145 /// 146 #[error("Failed to create passthrough device: {0}")] 147 CreatePassthroughDevice(#[source] anyhow::Error), 148 /// Write to Guest memory 149 /// 150 #[error("Failed to write to guest memory: {0}")] 151 GuestMemWrite(#[source] anyhow::Error), 152 /// 153 /// Read Guest memory 154 /// 155 #[error("Failed to read guest memory: {0}")] 156 GuestMemRead(#[source] anyhow::Error), 157 /// 158 /// Read from MMIO Bus 159 /// 160 #[error("Failed to read from MMIO Bus: {0}")] 161 MmioBusRead(#[source] anyhow::Error), 162 /// 163 /// Write to MMIO Bus 164 /// 165 #[error("Failed to write to MMIO Bus: {0}")] 166 MmioBusWrite(#[source] anyhow::Error), 167 /// 168 /// Read from IO Bus 169 /// 170 #[error("Failed to read from IO Bus: {0}")] 171 IoBusRead(#[source] anyhow::Error), 172 /// 173 /// Write to IO Bus 174 /// 175 #[error("Failed to write to IO Bus: {0}")] 176 IoBusWrite(#[source] anyhow::Error), 177 /// 178 /// Start dirty log error 179 /// 180 #[error("Failed to get dirty log: {0}")] 181 StartDirtyLog(#[source] anyhow::Error), 182 /// 183 /// Stop dirty log error 184 /// 185 #[error("Failed to get dirty log: {0}")] 186 StopDirtyLog(#[source] anyhow::Error), 187 /// 188 /// Get dirty log error 189 /// 190 #[error("Failed to get dirty log: {0}")] 191 GetDirtyLog(#[source] anyhow::Error), 192 /// 193 /// Assert virtual interrupt error 194 /// 195 #[error("Failed to assert virtual Interrupt: {0}")] 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: {0}")] 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: {0}")] 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: {0}")] 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: {0}")] 222 InitMemRegionTdx(#[source] std::io::Error), 223 /// 224 /// Create Vgic error 225 /// 226 #[error("Failed to create Vgic: {0}")] 227 CreateVgic(#[source] anyhow::Error), 228 /// 229 /// Create Vaia error 230 /// 231 #[error("Failed to create Vaia: {0}")] 232 CreateVaia(#[source] anyhow::Error), 233 /// 234 /// Import isolated pages error 235 /// 236 #[error("Failed to import isolated pages: {0}")] 237 ImportIsolatedPages(#[source] anyhow::Error), 238 /// Failed to complete isolated import 239 /// 240 #[error("Failed to complete isolated import: {0}")] 241 CompleteIsolatedImport(#[source] anyhow::Error), 242 /// Failed to set VM property 243 /// 244 #[error("Failed to set VM property: {0}")] 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: {0}")] 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: {0}")] 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. 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. 313 fn set_tss_address(&self, offset: usize) -> Result<()>; 314 #[cfg(not(target_arch = "riscv64"))] 315 /// Creates an in-kernel interrupt controller. 316 fn create_irq_chip(&self) -> Result<()>; 317 /// Registers an event that will, when signaled, trigger the `gsi` IRQ. 318 fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; 319 /// Unregister an event that will, when signaled, trigger the `gsi` IRQ. 320 fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; 321 /// Creates a new KVM vCPU file descriptor and maps the memory corresponding 322 fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>; 323 #[cfg(target_arch = "aarch64")] 324 fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>; 325 #[cfg(target_arch = "riscv64")] 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. 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. 336 fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>; 337 // Construct a routing entry 338 fn make_routing_entry(&self, gsi: u32, config: &InterruptSourceConfig) -> IrqRoutingEntry; 339 /// Sets the GSI routing table entries, overwriting any previously set 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 342 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. 352 fn create_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>; 353 /// Removes a guest physical memory slot. 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")] 357 fn get_preferred_target(&self, kvi: &mut crate::VcpuInit) -> Result<()>; 358 /// Enable split Irq capability 359 #[cfg(target_arch = "x86_64")] 360 fn enable_split_irq(&self) -> Result<()>; 361 #[cfg(target_arch = "x86_64")] 362 fn enable_sgx_attribute(&self, file: File) -> Result<()>; 363 /// Retrieve guest clock. 364 #[cfg(target_arch = "x86_64")] 365 fn get_clock(&self) -> Result<ClockData>; 366 /// Set guest clock. 367 #[cfg(target_arch = "x86_64")] 368 fn set_clock(&self, data: &ClockData) -> Result<()>; 369 /// Create a device that is used for passthrough 370 fn create_passthrough_device(&self) -> Result<vfio_ioctls::VfioDeviceFd>; 371 /// Start logging dirty pages 372 fn start_dirty_log(&self) -> Result<()>; 373 /// Stop logging dirty pages 374 fn stop_dirty_log(&self) -> Result<()>; 375 /// Get dirty pages bitmap 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 379 fn sev_snp_init(&self) -> Result<()> { 380 unimplemented!() 381 } 382 #[cfg(feature = "tdx")] 383 /// Initialize TDX on this VM 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 389 fn tdx_finalize(&self) -> Result<()> { 390 unimplemented!() 391 } 392 #[cfg(feature = "tdx")] 393 /// Initialize a TDX memory region for this VM 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 404 fn as_any(&self) -> &dyn Any; 405 /// Import the isolated pages 406 #[cfg(feature = "sev_snp")] 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")] 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 426 fn init(&self) -> Result<()> { 427 Ok(()) 428 } 429 /// Pause the VM 430 fn pause(&self) -> Result<()> { 431 Ok(()) 432 } 433 434 /// Resume the VM 435 fn resume(&self) -> Result<()> { 436 Ok(()) 437 } 438 439 #[cfg(feature = "sev_snp")] 440 fn gain_page_access(&self, _gpa: u64, _size: u32) -> Result<()> { 441 Ok(()) 442 } 443 } 444 445 pub trait VmOps: Send + Sync { 446 fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>; 447 fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>; 448 fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>; 449 fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>; 450 #[cfg(target_arch = "x86_64")] 451 fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>; 452 #[cfg(target_arch = "x86_64")] 453 fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>; 454 } 455