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