use alloc::{format, vec::Vec};
use ax_memory_addr::VirtAddr;
use axaddrspace::NestedPageTableOps;
use axvm_types::{VmArchPerCpuOps, VmArchVcpuOps, VmVcpuState};
use super::{BoundVcpuExit, VcpuRunAction};
use crate::{AxVmResult, ax_err};
pub(crate) trait ArchOps {
type VCpu: VmArchVcpuOps;
type PerCpu: VmArchPerCpuOps;
type DeferredRunWork;
type NestedPageTable: NestedPageTableOps;
fn has_hardware_support() -> bool;
fn clean_dcache_range(_addr: VirtAddr, _size: usize) {}
fn register_platform_irq_injector() {}
fn vcpu_affinities(
cpu_num: usize,
phys_cpu_ids: Option<&[usize]>,
phys_cpu_sets: Option<&[usize]>,
) -> Vec<(usize, Option<usize>, usize)> {
default_vcpu_affinities(cpu_num, phys_cpu_ids, phys_cpu_sets)
}
fn set_vcpu_on_args(vcpu: &crate::vm::AxVCpuRef<Self::VCpu>, _vcpu_id: usize, arg: usize) {
vcpu.set_gpr(0, arg);
}
fn before_first_run(_vm: &crate::AxVMRef, _vcpu: &crate::vm::AxVCpuRef<Self::VCpu>) {}
fn before_vcpu_run(_vm: &crate::AxVMRef, _vcpu: &crate::vm::AxVCpuRef<Self::VCpu>) {}
fn inject_pending_interrupt(
_vm: &crate::AxVMRef,
vcpu: &crate::vm::AxVCpuRef<Self::VCpu>,
interrupt: crate::vm::PendingInterrupt,
) {
match interrupt {
crate::vm::PendingInterrupt::Normal(vector) => {
trace!(
"Injecting queued interrupt {vector:#x} into VM[{}] VCpu[{}]",
vcpu.vm_id(),
vcpu.id()
);
if let Err(err) = vcpu.inject_interrupt(vector) {
warn!(
"Failed to inject queued interrupt {vector:#x} into VM[{}] VCpu[{}]: \
{err:?}",
vcpu.vm_id(),
vcpu.id()
);
}
}
crate::vm::PendingInterrupt::External {
vector,
physical_irq,
} => {
warn!(
"VM[{}] VCpu[{}] dropped unsupported external interrupt vector={vector:#x}, \
physical_irq={physical_irq:#x}",
vcpu.vm_id(),
vcpu.id()
);
}
}
}
fn after_external_interrupt(
_vm: &crate::AxVMRef,
_vcpu: &crate::vm::AxVCpuRef<Self::VCpu>,
vector: usize,
) {
crate::host::arceos::dispatch_host_irq(vector);
crate::check_timer_events();
}
fn on_last_vcpu_exit(_vm_id: usize) {}
fn after_mmio_write(_vm: &crate::AxVMRef) {}
fn handle_vcpu_exit_bound(
vm: &crate::AxVMRef,
vcpu: &crate::vm::AxVCpuRef<Self::VCpu>,
exit: <Self::VCpu as VmArchVcpuOps>::Exit,
) -> AxVmResult<BoundVcpuExit<Self::DeferredRunWork>>;
fn finish_deferred_run_work(
vm: &crate::AxVMRef,
vcpu: &crate::vm::AxVCpuRef<Self::VCpu>,
work: Self::DeferredRunWork,
) -> AxVmResult<VcpuRunAction>;
fn run_vcpu(
vm: &crate::AxVMRef,
vcpu: &crate::vm::AxVCpuRef<Self::VCpu>,
) -> AxVmResult<VcpuRunAction>
where
Self: Sized,
{
let vm_id = vm.id();
let vcpu_id = vcpu.id();
match vcpu.state() {
VmVcpuState::Free => vcpu.bind()?,
VmVcpuState::Ready => {}
state => {
return ax_err!(
BadState,
format!("VCpu state is not Free or Ready, but {state:?}")
);
}
}
let run_result = vcpu.with_current_cpu_set(|| -> AxVmResult<_> {
loop {
crate::runtime::vcpus::inject_pending_interrupts::<Self>(vm.id(), vcpu_id, vcpu);
let exit = vcpu.run()?;
trace!("{exit:#x?}");
match Self::handle_vcpu_exit_bound(vm, vcpu, exit)? {
BoundVcpuExit::Continue => continue,
action => break Ok(action),
}
}
});
let unbind_result = vcpu.unbind();
match run_result {
Ok(BoundVcpuExit::Complete(action)) => {
unbind_result?;
Ok(action)
}
Ok(BoundVcpuExit::Defer(work)) => {
unbind_result?;
Self::finish_deferred_run_work(vm, vcpu, work)
}
Ok(BoundVcpuExit::Continue) => unreachable!("continued exits do not leave run loop"),
Err(err) => {
if let Err(unbind_err) = unbind_result {
warn!(
"VM[{vm_id}] VCpu[{vcpu_id}] unbind after run error failed: {unbind_err:?}"
);
}
Err(err)
}
}
}
}
pub(crate) fn target_phys_cpu_ids(vcpu_mappings: &[(usize, Option<usize>, usize)]) -> Vec<usize> {
let mut cpu_ids = Vec::new();
for (_, maybe_mask, phys_id) in vcpu_mappings {
if let Some(mask) = maybe_mask {
for cpu_id in 0..usize::BITS as usize {
if mask & (1usize << cpu_id) != 0 && !cpu_ids.contains(&cpu_id) {
cpu_ids.push(cpu_id);
}
}
} else if !cpu_ids.contains(phys_id) {
cpu_ids.push(*phys_id);
}
}
cpu_ids
}
pub(crate) fn default_vcpu_affinities(
cpu_num: usize,
phys_cpu_ids: Option<&[usize]>,
phys_cpu_sets: Option<&[usize]>,
) -> Vec<(usize, Option<usize>, usize)> {
let mut vcpus = Vec::with_capacity(cpu_num);
for vcpu_id in 0..cpu_num {
vcpus.push((vcpu_id, None, vcpu_id));
}
if let Some(phys_cpu_sets) = phys_cpu_sets {
for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() {
if let Some(vcpu) = vcpus.get_mut(vcpu_id) {
vcpu.1 = Some(*pcpu_mask_bitmap);
}
}
}
if let Some(phys_cpu_ids) = phys_cpu_ids {
for (vcpu_id, phys_id) in phys_cpu_ids.iter().enumerate() {
if let Some(vcpu) = vcpus.get_mut(vcpu_id) {
vcpu.2 = *phys_id;
}
}
}
vcpus
}