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