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 use crate::cpu::Vcpu; 14 use crate::device::Device; 15 #[cfg(feature = "tdx")] 16 use crate::x86_64::CpuId; 17 #[cfg(all(feature = "kvm", target_arch = "x86_64"))] 18 use crate::ClockData; 19 #[cfg(feature = "kvm")] 20 use crate::CreateDevice; 21 #[cfg(feature = "mshv")] 22 use crate::HvState as VmState; 23 #[cfg(feature = "kvm")] 24 use crate::KvmVmState as VmState; 25 use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion}; 26 #[cfg(feature = "kvm")] 27 use kvm_ioctls::Cap; 28 #[cfg(target_arch = "x86_64")] 29 use std::fs::File; 30 use std::sync::Arc; 31 use thiserror::Error; 32 use vmm_sys_util::eventfd::EventFd; 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 /// TSS address error 63 /// 64 #[error("Failed to set TSS address: {0}")] 65 SetTssAddress(#[source] anyhow::Error), 66 /// 67 /// Create interrupt controller error 68 /// 69 #[error("Failed to create interrupt controller: {0}")] 70 CreateIrq(#[source] anyhow::Error), 71 /// 72 /// Register interrupt event error 73 /// 74 #[error("Failed to register interrupt event: {0}")] 75 RegisterIrqFd(#[source] anyhow::Error), 76 /// 77 /// Un register interrupt event error 78 /// 79 #[error("Failed to unregister interrupt event: {0}")] 80 UnregisterIrqFd(#[source] anyhow::Error), 81 /// 82 /// Register IO event error 83 /// 84 #[error("Failed to register IO event: {0}")] 85 RegisterIoEvent(#[source] anyhow::Error), 86 /// 87 /// Unregister IO event error 88 /// 89 #[error("Failed to unregister IO event: {0}")] 90 UnregisterIoEvent(#[source] anyhow::Error), 91 /// 92 /// Set GSI routing error 93 /// 94 #[error("Failed to set GSI routing: {0}")] 95 SetGsiRouting(#[source] anyhow::Error), 96 /// 97 /// Create user memory error 98 /// 99 #[error("Failed to create user memory: {0}")] 100 CreateUserMemory(#[source] anyhow::Error), 101 /// 102 /// Remove user memory region error 103 /// 104 #[error("Failed to remove user memory: {0}")] 105 RemoveUserMemory(#[source] anyhow::Error), 106 /// 107 /// Create device error 108 /// 109 #[error("Failed to set GSI routing: {0}")] 110 CreateDevice(#[source] anyhow::Error), 111 /// 112 /// Get preferred target error 113 /// 114 #[error("Failed to get preferred target: {0}")] 115 GetPreferredTarget(#[source] anyhow::Error), 116 /// 117 /// Enable split Irq error 118 /// 119 #[error("Failed to enable split Irq: {0}")] 120 EnableSplitIrq(#[source] anyhow::Error), 121 /// 122 /// Enable SGX attribute error 123 /// 124 #[error("Failed to enable SGX attribute: {0}")] 125 EnableSgxAttribute(#[source] anyhow::Error), 126 /// 127 /// Get clock error 128 /// 129 #[error("Failed to get clock: {0}")] 130 GetClock(#[source] anyhow::Error), 131 /// 132 /// Set clock error 133 /// 134 #[error("Failed to set clock: {0}")] 135 SetClock(#[source] anyhow::Error), 136 /// 137 /// Create passthrough device 138 /// 139 #[error("Failed to create passthrough device: {0}")] 140 CreatePassthroughDevice(#[source] anyhow::Error), 141 /// Write to Guest memory 142 /// 143 #[error("Failed to write to guest memory: {0}")] 144 GuestMemWrite(#[source] anyhow::Error), 145 /// 146 /// Read Guest memory 147 /// 148 #[error("Failed to read guest memory: {0}")] 149 GuestMemRead(#[source] anyhow::Error), 150 /// 151 /// Read from MMIO Bus 152 /// 153 #[error("Failed to read from MMIO Bus: {0}")] 154 MmioBusRead(#[source] anyhow::Error), 155 /// 156 /// Write to MMIO Bus 157 /// 158 #[error("Failed to write to MMIO Bus: {0}")] 159 MmioBusWrite(#[source] anyhow::Error), 160 /// 161 /// Read from IO Bus 162 /// 163 #[error("Failed to read from IO Bus: {0}")] 164 IoBusRead(#[source] anyhow::Error), 165 /// 166 /// Write to IO Bus 167 /// 168 #[error("Failed to write to IO Bus: {0}")] 169 IoBusWrite(#[source] anyhow::Error), 170 /// 171 /// Start dirty log error 172 /// 173 #[error("Failed to get dirty log: {0}")] 174 StartDirtyLog(#[source] anyhow::Error), 175 /// 176 /// Stop dirty log error 177 /// 178 #[error("Failed to get dirty log: {0}")] 179 StopDirtyLog(#[source] anyhow::Error), 180 /// 181 /// Get dirty log error 182 /// 183 #[error("Failed to get dirty log: {0}")] 184 GetDirtyLog(#[source] anyhow::Error), 185 /// 186 /// Assert virtual interrupt error 187 /// 188 #[error("Failed to assert virtual Interrupt: {0}")] 189 AsserttVirtualInterrupt(#[source] anyhow::Error), 190 191 #[cfg(feature = "tdx")] 192 /// 193 /// Error initializing TDX on the VM 194 /// 195 #[error("Failed to initialize TDX: {0}")] 196 InitializeTdx(#[source] std::io::Error), 197 #[cfg(feature = "tdx")] 198 /// 199 /// Error finalizing the TDX configuration on the VM 200 /// 201 #[error("Failed to finalize TDX: {0}")] 202 FinalizeTdx(#[source] std::io::Error), 203 #[cfg(feature = "tdx")] 204 /// 205 /// Error initializing the TDX memory region 206 /// 207 #[error("Failed to initialize memory region TDX: {0}")] 208 InitMemRegionTdx(#[source] std::io::Error), 209 } 210 /// 211 /// Result type for returning from a function 212 /// 213 pub type Result<T> = std::result::Result<T, HypervisorVmError>; 214 215 /// 216 /// Trait to represent a Vm 217 /// 218 /// This crate provides a hypervisor-agnostic interfaces for Vm 219 /// 220 pub trait Vm: Send + Sync { 221 #[cfg(target_arch = "x86_64")] 222 /// Sets the address of the three-page region in the VM's address space. 223 fn set_tss_address(&self, offset: usize) -> Result<()>; 224 /// Creates an in-kernel interrupt controller. 225 fn create_irq_chip(&self) -> Result<()>; 226 /// Registers an event that will, when signaled, trigger the `gsi` IRQ. 227 fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; 228 /// Unregister an event that will, when signaled, trigger the `gsi` IRQ. 229 fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; 230 /// Creates a new KVM vCPU file descriptor and maps the memory corresponding 231 fn create_vcpu(&self, id: u8, vmmops: Option<Arc<dyn VmmOps>>) -> Result<Arc<dyn Vcpu>>; 232 /// Registers an event to be signaled whenever a certain address is written to. 233 fn register_ioevent( 234 &self, 235 fd: &EventFd, 236 addr: &IoEventAddress, 237 datamatch: Option<DataMatch>, 238 ) -> Result<()>; 239 /// Unregister an event from a certain address it has been previously registered to. 240 fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> Result<()>; 241 /// Sets the GSI routing table entries, overwriting any previously set 242 fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> Result<()>; 243 /// Creates a memory region structure that can be used with {create/remove}_user_memory_region 244 fn make_user_memory_region( 245 &self, 246 slot: u32, 247 guest_phys_addr: u64, 248 memory_size: u64, 249 userspace_addr: u64, 250 readonly: bool, 251 log_dirty_pages: bool, 252 ) -> MemoryRegion; 253 /// Creates a guest physical memory slot. 254 fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>; 255 /// Removes a guest physical memory slot. 256 fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>; 257 #[cfg(feature = "kvm")] 258 /// Creates an emulated device in the kernel. 259 fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>; 260 /// Returns the preferred CPU target type which can be emulated by KVM on underlying host. 261 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] 262 fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>; 263 /// Enable split Irq capability 264 #[cfg(target_arch = "x86_64")] 265 fn enable_split_irq(&self) -> Result<()>; 266 #[cfg(target_arch = "x86_64")] 267 fn enable_sgx_attribute(&self, file: File) -> Result<()>; 268 /// Retrieve guest clock. 269 #[cfg(all(feature = "kvm", target_arch = "x86_64"))] 270 fn get_clock(&self) -> Result<ClockData>; 271 /// Set guest clock. 272 #[cfg(all(feature = "kvm", target_arch = "x86_64"))] 273 fn set_clock(&self, data: &ClockData) -> Result<()>; 274 #[cfg(feature = "kvm")] 275 /// Checks if a particular `Cap` is available. 276 fn check_extension(&self, c: Cap) -> bool; 277 /// Create a device that is used for passthrough 278 fn create_passthrough_device(&self) -> Result<Arc<dyn Device>>; 279 /// Get the Vm state. Return VM specific data 280 fn state(&self) -> Result<VmState>; 281 /// Set the VM state 282 fn set_state(&self, state: VmState) -> Result<()>; 283 /// Start logging dirty pages 284 fn start_dirty_log(&self) -> Result<()>; 285 /// Stop logging dirty pages 286 fn stop_dirty_log(&self) -> Result<()>; 287 /// Get dirty pages bitmap 288 fn get_dirty_log(&self, slot: u32, base_gpa: u64, memory_size: u64) -> Result<Vec<u64>>; 289 #[cfg(feature = "tdx")] 290 /// Initalize TDX on this VM 291 fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>; 292 #[cfg(feature = "tdx")] 293 /// Finalize the configuration of TDX on this VM 294 fn tdx_finalize(&self) -> Result<()>; 295 #[cfg(feature = "tdx")] 296 /// Initalize a TDX memory region for this VM 297 fn tdx_init_memory_region( 298 &self, 299 host_address: u64, 300 guest_address: u64, 301 size: u64, 302 measure: bool, 303 ) -> Result<()>; 304 } 305 306 pub trait VmmOps: Send + Sync { 307 fn guest_mem_write(&self, gpa: u64, buf: &[u8]) -> Result<usize>; 308 fn guest_mem_read(&self, gpa: u64, buf: &mut [u8]) -> Result<usize>; 309 fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<()>; 310 fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<()>; 311 #[cfg(target_arch = "x86_64")] 312 fn pio_read(&self, port: u64, data: &mut [u8]) -> Result<()>; 313 #[cfg(target_arch = "x86_64")] 314 fn pio_write(&self, port: u64, data: &[u8]) -> Result<()>; 315 } 316