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