extern crate alloc;
use alloc::{collections::BTreeMap, vec::Vec};
use ax_errno::{AxResult, ax_err, ax_err_type};
use ax_kspin::SpinNoIrq as Mutex;
use axvcpu::get_current_vcpu;
use axvm_types::VMId;
use crate::{
host::{HostPlatform, default_host},
vcpu::AxArchVCpuImpl,
vm::AxVMRef,
};
pub struct AxvmRuntime {
_private: (),
}
static VM_REGISTRY: Mutex<BTreeMap<VMId, AxVMRef>> = Mutex::new(BTreeMap::new());
pub(crate) fn push_existing_vm(vm: AxVMRef) -> bool {
let vm_id = vm.id();
let mut registry = VM_REGISTRY.lock();
if registry.contains_key(&vm_id) {
warn!("VM[{vm_id}] already exists, push VM failed");
return false;
}
registry.insert(vm_id, vm);
true
}
pub(crate) fn remove_existing_vm(vm_id: VMId) -> Option<AxVMRef> {
let vm = VM_REGISTRY.lock().remove(&vm_id)?;
crate::runtime::vcpus::cleanup_vm_vcpus(vm_id);
Some(vm)
}
pub fn get_vm_by_id(vm_id: VMId) -> Option<AxVMRef> {
VM_REGISTRY.lock().get(&vm_id).cloned()
}
pub fn get_vm_list() -> Vec<AxVMRef> {
VM_REGISTRY.lock().values().cloned().collect()
}
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
pub(crate) fn with_vm<F, R>(vm_id: VMId, f: F) -> Option<R>
where
F: FnOnce(&AxVMRef) -> R,
{
let vm = VM_REGISTRY.lock().get(&vm_id).cloned();
vm.map(|vm| f(&vm))
}
#[cfg(target_arch = "x86_64")]
pub(crate) fn active_vcpu_mask(vm_id: VMId) -> Option<usize> {
with_vm(vm_id, |vm| {
let vcpu_num = vm.vcpu_num();
if vcpu_num >= usize::BITS as usize {
usize::MAX
} else {
(1usize << vcpu_num) - 1
}
})
}
#[cfg(target_arch = "x86_64")]
pub(crate) fn inject_interrupt(vm_id: VMId, vcpu_id: usize, vector: usize) -> AxResult {
crate::runtime::vcpus::queue_interrupt(vm_id, vcpu_id, vector)
}
pub fn current_vm_id() -> Option<VMId> {
get_current_vcpu::<AxArchVCpuImpl>().map(|vcpu| vcpu.vm_id())
}
pub fn current_vcpu_id() -> Option<usize> {
get_current_vcpu::<AxArchVCpuImpl>().map(|vcpu| vcpu.id())
}
pub fn inject_current_vcpu_interrupt(vector: usize) -> AxResult {
let vcpu = get_current_vcpu::<AxArchVCpuImpl>()
.ok_or_else(|| ax_err_type!(BadState, "current vCPU is not set"))?;
vcpu.inject_interrupt(vector)
}
impl AxvmRuntime {
pub fn new() -> AxResult<Self> {
let host = default_host();
if !host.has_hardware_support() {
return ax_err!(Unsupported, "hardware virtualization is not supported");
}
host.enable_virtualization_on_all_cpus()?;
Ok(Self { _private: () })
}
pub fn init_vms(&self) {
crate::runtime::init();
}
pub fn start_default_vms(&self) {
crate::runtime::start();
}
pub fn with_vm<T>(vm_id: VMId, f: impl FnOnce(AxVMRef) -> T) -> Option<T> {
crate::get_vm_by_id(vm_id).map(f)
}
pub fn start_vm(vm_id: VMId) -> AxResult {
crate::runtime::start_vm(vm_id)
}
pub fn stop_vm(vm_id: VMId) -> AxResult {
crate::runtime::stop_vm(vm_id)
}
pub fn resume_vm(vm_id: VMId) -> AxResult {
crate::runtime::resume_vm(vm_id)
}
pub fn remove_vm(vm_id: VMId) -> Option<AxVMRef> {
crate::runtime::remove_vm(vm_id)
}
}
pub fn register_vm(vm: AxVMRef) -> bool {
crate::runtime::register_vm(vm)
}