use std::sync::atomic::Ordering;
use crate::{
error::HypervisorError,
traits::VirtualMachine,
types::{DeviceSnapshot, VirtioDeviceConfig},
};
use super::virtio::{VIRTIO_MMIO_SIZE, bincode_serialize_device_config};
use super::{KvmMemory, KvmVcpu, KvmVm, VirtioDeviceInfo, VmState};
impl VirtualMachine for KvmVm {
type Vcpu = KvmVcpu;
type Memory = KvmMemory;
fn memory(&self) -> &Self::Memory {
&self.memory
}
fn create_vcpu(&mut self, id: u32) -> Result<Self::Vcpu, HypervisorError> {
if id >= self.config.vcpu_count {
return Err(HypervisorError::VcpuCreationFailed {
id,
reason: format!(
"vCPU ID {} exceeds configured count {}",
id, self.config.vcpu_count
),
});
}
{
let vcpus = self
.vcpus
.read()
.map_err(|_| HypervisorError::VcpuCreationFailed {
id,
reason: "Lock poisoned".to_string(),
})?;
if vcpus.contains(&id) {
return Err(HypervisorError::VcpuCreationFailed {
id,
reason: "vCPU already created".to_string(),
});
}
}
let vcpu_fd = self
.vm_fd
.create_vcpu(id, self.vcpu_mmap_size)
.map_err(|e| HypervisorError::VcpuCreationFailed {
id,
reason: format!("KVM error: {}", e),
})?;
let vcpu = KvmVcpu::new(id, vcpu_fd)?;
{
let mut vcpus =
self.vcpus
.write()
.map_err(|_| HypervisorError::VcpuCreationFailed {
id,
reason: "Lock poisoned".to_string(),
})?;
vcpus.push(id);
}
tracing::debug!("Created vCPU {} for VM {}", id, self.id);
Ok(vcpu)
}
fn add_virtio_device(&mut self, device: VirtioDeviceConfig) -> Result<(), HypervisorError> {
let state = self.state();
if state != VmState::Created {
return Err(HypervisorError::DeviceError(
"Cannot add device: VM not in Created state".to_string(),
));
}
let mmio_base = self.allocate_mmio_region()?;
let gsi = self.allocate_irq();
let irq_fd = Self::create_eventfd()?;
if let Err(e) = self.register_irqfd(irq_fd, gsi, None) {
unsafe { libc::close(irq_fd) };
return Err(e);
}
let notify_fd = match self.setup_ioeventfd(mmio_base) {
Ok(fd) => fd,
Err(e) => {
let _ = self.unregister_irqfd(irq_fd, gsi);
unsafe { libc::close(irq_fd) };
return Err(e);
}
};
let device_info = VirtioDeviceInfo {
device_type: device.device_type.clone(),
mmio_base,
mmio_size: VIRTIO_MMIO_SIZE,
irq: gsi,
irq_fd,
notify_fd,
};
{
let mut devices = self.virtio_devices.write().map_err(|_| {
let _ = self.unregister_irqfd(irq_fd, gsi);
unsafe {
libc::close(irq_fd);
libc::close(notify_fd);
}
HypervisorError::DeviceError("Lock poisoned".to_string())
})?;
devices.push(device_info);
}
tracing::info!(
"Added {:?} device to VM {}: MMIO={:#x}-{:#x}, IRQ={}, irq_fd={}, notify_fd={}",
device.device_type,
self.id,
mmio_base,
mmio_base + VIRTIO_MMIO_SIZE,
gsi,
irq_fd,
notify_fd
);
Ok(())
}
fn start(&mut self) -> Result<(), HypervisorError> {
let state = self.state();
if state != VmState::Created && state != VmState::Stopped {
return Err(HypervisorError::VmStateError {
expected: "Created or Stopped".to_string(),
actual: format!("{:?}", state),
});
}
self.set_state(VmState::Starting);
self.running.store(true, Ordering::SeqCst);
self.set_state(VmState::Running);
tracing::info!("Started VM {}", self.id);
Ok(())
}
fn pause(&mut self) -> Result<(), HypervisorError> {
let state = self.state();
if state != VmState::Running {
return Err(HypervisorError::VmStateError {
expected: "Running".to_string(),
actual: format!("{:?}", state),
});
}
self.set_state(VmState::Paused);
tracing::info!("Paused VM {}", self.id);
Ok(())
}
fn resume(&mut self) -> Result<(), HypervisorError> {
let state = self.state();
if state != VmState::Paused {
return Err(HypervisorError::VmStateError {
expected: "Paused".to_string(),
actual: format!("{:?}", state),
});
}
self.set_state(VmState::Running);
tracing::info!("Resumed VM {}", self.id);
Ok(())
}
fn stop(&mut self) -> Result<(), HypervisorError> {
let state = self.state();
if state != VmState::Running && state != VmState::Paused {
return Err(HypervisorError::VmStateError {
expected: "Running or Paused".to_string(),
actual: format!("{:?}", state),
});
}
self.set_state(VmState::Stopping);
self.running.store(false, Ordering::SeqCst);
self.set_state(VmState::Stopped);
tracing::info!("Stopped VM {}", self.id);
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn vcpu_count(&self) -> u32 {
self.config.vcpu_count
}
fn snapshot_devices(&self) -> Result<Vec<DeviceSnapshot>, HypervisorError> {
let devices = self
.virtio_devices
.read()
.map_err(|_| HypervisorError::SnapshotError("Lock poisoned".to_string()))?;
let mut snapshots = Vec::with_capacity(devices.len());
for device in devices.iter() {
let state_bytes = bincode_serialize_device_config(device);
snapshots.push(DeviceSnapshot {
device_type: device.device_type.clone(),
name: format!("{:?}-{}", device.device_type, snapshots.len()),
state: state_bytes,
});
}
tracing::debug!(
"snapshot_devices: captured {} device configurations",
snapshots.len()
);
Ok(snapshots)
}
fn restore_devices(&mut self, snapshots: &[DeviceSnapshot]) -> Result<(), HypervisorError> {
let devices = self
.virtio_devices
.read()
.map_err(|_| HypervisorError::SnapshotError("Lock poisoned".to_string()))?;
if snapshots.len() != devices.len() {
return Err(HypervisorError::SnapshotError(format!(
"Device count mismatch: snapshot has {}, VM has {}",
snapshots.len(),
devices.len()
)));
}
for (snapshot, device) in snapshots.iter().zip(devices.iter()) {
if snapshot.device_type != device.device_type {
return Err(HypervisorError::SnapshotError(format!(
"Device type mismatch: snapshot has {:?}, VM has {:?}",
snapshot.device_type, device.device_type
)));
}
}
tracing::debug!(
"restore_devices: verified {} device configurations",
snapshots.len()
);
Ok(())
}
}