mod dirty;
mod drop;
mod irq;
#[cfg(test)]
mod tests;
mod virtio;
mod virtual_machine;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
use std::sync::{Arc, RwLock};
use crate::{config::VmConfig, error::HypervisorError};
#[cfg(target_arch = "x86_64")]
use super::ffi::KvmPitConfig;
use super::ffi::{self, KvmSystem, KvmUserspaceMemoryRegion, KvmVmFd};
use super::memory::KvmMemory;
pub use virtio::VirtioDeviceInfo;
static VM_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone)]
pub(super) struct MemorySlotInfo {
slot: u32,
guest_phys_addr: u64,
size: u64,
userspace_addr: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmState {
Created,
Starting,
Running,
Paused,
Stopping,
Stopped,
Error,
}
pub struct KvmVm {
id: u64,
config: VmConfig,
#[allow(dead_code)]
kvm: Arc<KvmSystem>,
vm_fd: KvmVmFd,
vcpu_mmap_size: usize,
memory: KvmMemory,
next_slot: AtomicU32,
vcpus: RwLock<Vec<u32>>,
state: RwLock<VmState>,
running: AtomicBool,
virtio_devices: RwLock<Vec<VirtioDeviceInfo>>,
next_virtio_irq: AtomicU32,
memory_slots: RwLock<Vec<MemorySlotInfo>>,
dirty_tracking_enabled: AtomicBool,
}
unsafe impl Send for KvmVm {}
unsafe impl Sync for KvmVm {}
impl KvmVm {
pub(crate) fn new(
kvm: Arc<KvmSystem>,
vcpu_mmap_size: usize,
config: VmConfig,
) -> Result<Self, HypervisorError> {
let id = VM_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
let vm_fd = kvm.create_vm().map_err(|e| {
HypervisorError::VmCreationFailed(format!("Failed to create KVM VM: {}", e))
})?;
#[cfg(target_arch = "x86_64")]
Self::setup_x86_vm(&vm_fd)?;
let memory = KvmMemory::new(config.memory_size)?;
let region = KvmUserspaceMemoryRegion {
slot: 0,
flags: 0,
guest_phys_addr: 0,
memory_size: config.memory_size,
userspace_addr: memory.host_address() as u64,
};
vm_fd.set_user_memory_region(®ion).map_err(|e| {
HypervisorError::VmCreationFailed(format!("Failed to map guest memory: {}", e))
})?;
let main_slot = MemorySlotInfo {
slot: 0,
guest_phys_addr: 0,
size: config.memory_size,
userspace_addr: memory.host_address() as u64,
};
memory.attach_vm_fd(vm_fd.as_raw_fd());
memory.register_slot(
main_slot.slot,
main_slot.guest_phys_addr,
main_slot.size,
main_slot.userspace_addr,
0,
)?;
tracing::info!(
"Created KVM VM {}: vcpus={}, memory={}MB",
id,
config.vcpu_count,
config.memory_size / (1024 * 1024)
);
Ok(Self {
id,
config,
kvm,
vm_fd,
vcpu_mmap_size,
memory,
next_slot: AtomicU32::new(1), vcpus: RwLock::new(Vec::new()),
state: RwLock::new(VmState::Created),
running: AtomicBool::new(false),
virtio_devices: RwLock::new(Vec::new()),
next_virtio_irq: AtomicU32::new(0),
memory_slots: RwLock::new(vec![main_slot]),
dirty_tracking_enabled: AtomicBool::new(false),
})
}
#[cfg(target_arch = "x86_64")]
fn setup_x86_vm(vm_fd: &KvmVmFd) -> Result<(), HypervisorError> {
const TSS_ADDR: u64 = 0xfffb_d000;
vm_fd
.set_tss_addr(TSS_ADDR)
.map_err(|e| HypervisorError::VmCreationFailed(format!("Failed to set TSS: {}", e)))?;
const IDENTITY_MAP_ADDR: u64 = 0xfffb_c000;
vm_fd
.set_identity_map_addr(IDENTITY_MAP_ADDR)
.map_err(|e| {
HypervisorError::VmCreationFailed(format!("Failed to set identity map: {}", e))
})?;
vm_fd.create_irqchip().map_err(|e| {
HypervisorError::VmCreationFailed(format!("Failed to create IRQ chip: {}", e))
})?;
let pit_config = KvmPitConfig::default();
vm_fd.create_pit2(&pit_config).map_err(|e| {
HypervisorError::VmCreationFailed(format!("Failed to create PIT: {}", e))
})?;
Ok(())
}
#[must_use]
pub fn id(&self) -> u64 {
self.id
}
#[must_use]
pub fn config(&self) -> &VmConfig {
&self.config
}
pub fn state(&self) -> VmState {
*self.state.read().unwrap()
}
#[must_use]
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
pub(super) fn set_state(&self, new_state: VmState) {
let mut state = self.state.write().unwrap();
tracing::debug!("VM {} state: {:?} -> {:?}", self.id, *state, new_state);
*state = new_state;
}
pub(crate) fn vm_fd(&self) -> &KvmVmFd {
&self.vm_fd
}
pub(crate) fn vcpu_mmap_size(&self) -> usize {
self.vcpu_mmap_size
}
}