pub(crate) mod hvc;
mod ivc;
pub(crate) mod vcpus;
mod dispatcher;
mod queue;
use core::sync::atomic::{AtomicUsize, Ordering};
#[allow(unused_imports)]
pub(crate) use dispatcher::VcpuIrqDispatcher;
use crate::{AxVmError, AxVmResult, StopReason, VmStatus, ax_err};
pub type VMRef = crate::AxVMRef;
pub type VCpuRef = crate::vm::AxVCpuRef;
static VMM: crate::HostWaitQueueHandle = crate::HostWaitQueueHandle::new();
static RUNNING_VM_COUNT: AtomicUsize = AtomicUsize::new(0);
pub fn init() {
info!("Initializing VMM...");
}
pub fn start() {
info!("VMM starting, booting VMs...");
for vm in crate::get_vm_list() {
match vm.start() {
Ok(_) => {
RUNNING_VM_COUNT.fetch_add(1, Ordering::Release);
vcpus::notify_primary_vcpu(vm.id());
info!("VM[{}] boot success", vm.id())
}
Err(err) => warn!("VM[{}] boot failed, error {:?}", vm.id(), err),
}
}
crate::host::task::wait_queue_wait_until(&VMM, || {
let vm_count = RUNNING_VM_COUNT.load(Ordering::Acquire);
debug!("a VM exited, current running VM count: {vm_count}");
vm_count == 0
});
}
pub fn add_running_vm_count(count: usize) {
RUNNING_VM_COUNT.fetch_add(count, Ordering::Release);
}
pub fn sub_running_vm_count(count: usize) {
RUNNING_VM_COUNT.fetch_sub(count, Ordering::Release);
}
fn reset_starts_counted_runtime(previous_status: VmStatus) -> bool {
matches!(
previous_status,
VmStatus::Ready
| VmStatus::Running
| VmStatus::Paused
| VmStatus::Stopping
| VmStatus::Stopped
)
}
pub fn start_vm(vm_id: usize) -> AxVmResult {
let vm = vm_by_id(vm_id)?;
let status = vm.status();
if !matches!(status, VmStatus::Ready | VmStatus::Stopped) {
return ax_err!(BadState, "VM cannot be started from its current state");
}
vm.start()?;
add_running_vm_count(1);
vcpus::notify_primary_vcpu(vm_id);
Ok(())
}
pub fn stop_vm(vm_id: usize) -> AxVmResult {
let vm = vm_by_id(vm_id)?;
vm.stop(StopReason::Forced)?;
vcpus::notify_all_vcpus(vm_id);
Ok(())
}
pub fn resume_vm(vm_id: usize) -> AxVmResult {
let vm = vm_by_id(vm_id)?;
vm.resume()?;
vcpus::notify_all_vcpus(vm_id);
Ok(())
}
pub fn reset_vm(vm_id: usize) -> AxVmResult {
let vm = vm_by_id(vm_id)?;
let previous_status = vm.status();
vm.reset()?;
if reset_starts_counted_runtime(previous_status) {
add_running_vm_count(1);
}
vcpus::notify_primary_vcpu(vm_id);
Ok(())
}
pub fn remove_vm(vm_id: usize) -> Option<VMRef> {
crate::manager::remove_existing_vm(vm_id)
}
pub fn register_vm(vm: VMRef) -> bool {
crate::manager::push_existing_vm(vm)
}
fn vm_by_id(vm_id: usize) -> AxVmResult<VMRef> {
crate::get_vm_by_id(vm_id).ok_or_else(|| missing_vm_error(vm_id))
}
const fn missing_vm_error(vm_id: usize) -> AxVmError {
AxVmError::VmNotFound { vm_id }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reset_counts_replacement_runtime_for_every_restartable_state() {
for status in [
VmStatus::Ready,
VmStatus::Running,
VmStatus::Paused,
VmStatus::Stopping,
VmStatus::Stopped,
] {
assert!(
reset_starts_counted_runtime(status),
"reset from {status:?} starts a fresh running runtime"
);
}
}
#[test]
fn missing_vm_is_reported_with_its_id() {
let vm_id = usize::MAX;
assert_eq!(missing_vm_error(vm_id), AxVmError::VmNotFound { vm_id });
}
}