use std::{sync::Arc, vec::Vec};
use ax_std::os::arceos::modules::ax_task::IrqNotify;
use crate::AxVmResult;
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub(crate) enum TargetCpuCapabilityError {
#[error("no target physical CPU is configured for {capability}")]
NoTargets { capability: &'static str },
#[error("target CPU {cpu_id} has no recorded {capability}")]
Missing {
capability: &'static str,
cpu_id: usize,
},
}
pub(crate) fn minimum_recorded_target_cpu_capability(
capability: &'static str,
vcpu_mappings: &[(usize, Option<usize>, usize)],
capability_for_cpu: impl FnMut(usize) -> Option<u64>,
) -> Result<u64, TargetCpuCapabilityError> {
let capabilities =
recorded_target_cpu_capabilities(capability, vcpu_mappings, capability_for_cpu)?;
let mut capabilities = capabilities.into_iter();
let (_, first) = capabilities
.next()
.ok_or(TargetCpuCapabilityError::NoTargets { capability })?;
Ok(capabilities.fold(first, |minimum, (_, value)| minimum.min(value)))
}
pub(crate) fn recorded_target_cpu_capabilities(
capability: &'static str,
vcpu_mappings: &[(usize, Option<usize>, usize)],
mut capability_for_cpu: impl FnMut(usize) -> Option<u64>,
) -> Result<Vec<(usize, u64)>, TargetCpuCapabilityError> {
let cpu_ids = crate::architecture::ops::target_phys_cpu_ids(vcpu_mappings);
if cpu_ids.is_empty() {
return Err(TargetCpuCapabilityError::NoTargets { capability });
}
cpu_ids
.into_iter()
.map(|cpu_id| {
capability_for_cpu(cpu_id)
.map(|value| (cpu_id, value))
.ok_or(TargetCpuCapabilityError::Missing { capability, cpu_id })
})
.collect()
}
pub(crate) fn unsupported_target_cpu_capability(
operation: &'static str,
error: TargetCpuCapabilityError,
) -> crate::AxVmError {
crate::AxVmError::unsupported(operation, error)
}
pub(crate) trait MachinePlatform {
const MACHINE_ARCHITECTURE: crate::machine::MachineArchitecture;
}
pub(crate) trait GuestBootPlatform {
fn init_guest_boot_resources() {}
fn prepare_guest_boot(
_vm_config: &mut crate::config::AxVMConfig,
_vm_create_config: &mut axvmconfig::GuestConfig,
_provider: &dyn crate::boot::BootImageProvider,
) -> AxVmResult<Option<crate::boot::fdt::GuestDtbImage>> {
Ok(None)
}
}
pub(crate) trait BootImagePlatform {
fn default_boot_firmware_load_gpa(
_config: &axvmconfig::GuestConfig,
) -> Option<axvm_types::GuestPhysAddr> {
None
}
fn load_images_from_memory(
loader: &mut crate::boot::images::ImageLoaderCore<'_>,
images: crate::boot::StaticVmImage,
) -> AxVmResult {
loader.load_standard_images_from_memory(images, Self::load_guest_dtb)
}
#[cfg(any(feature = "fs", feature = "host-fs"))]
fn load_images_from_filesystem(
loader: &mut crate::boot::images::ImageLoaderCore<'_>,
) -> AxVmResult {
loader.load_standard_images_from_filesystem(Self::load_guest_dtb)
}
fn load_guest_dtb(
_loader: &crate::boot::images::ImageLoaderCore<'_>,
_dtb: &crate::boot::fdt::GuestDtbImage,
) -> AxVmResult {
Ok(())
}
fn is_x86_linux_image_config(
_config: &axvmconfig::GuestConfig,
_provider: &dyn crate::boot::BootImageProvider,
) -> bool {
false
}
}
pub(crate) trait HostTimePlatform {
fn request_timer_deadline(deadline_ns: u64) {
ax_std::os::arceos::modules::ax_task::request_timer_deadline_nanos(deadline_ns);
}
fn register_timer_source(
deadline_source: Arc<crate::timer::PublishedTimerDeadline>,
notify: Arc<IrqNotify>,
) {
let published_deadline = deadline_source.clone();
ax_std::os::arceos::modules::ax_task::register_timer_deadline_source(move || {
published_deadline.deadline_nanos()
});
ax_std::os::arceos::modules::ax_task::register_timer_irq_callback(move |now| {
deadline_source.clear_if_elapsed(now.as_nanos().min(u64::MAX as u128) as u64);
notify.notify_irq();
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recorded_minimum_includes_every_cpu_in_vcpu_affinity_masks() {
let mappings = [
(0, Some((1 << 0) | (1 << 2)), 0),
(1, None, 1),
(2, Some((1 << 2) | (1 << 3)), 0),
];
let capabilities = [48, 44, 42, 39];
assert_eq!(
minimum_recorded_target_cpu_capability("IPA bits", &mappings, |cpu_id| {
Some(capabilities[cpu_id])
}),
Ok(39)
);
}
#[test]
fn recorded_minimum_rejects_an_empty_target_set() {
assert_eq!(
minimum_recorded_target_cpu_capability("IPA bits", &[], |_| Some(48)),
Err(TargetCpuCapabilityError::NoTargets {
capability: "IPA bits",
})
);
}
#[test]
fn recorded_minimum_rejects_an_uninitialized_target_cpu() {
let mappings = [(0, Some((1 << 0) | (1 << 2)), 0)];
assert_eq!(
minimum_recorded_target_cpu_capability("IPA bits", &mappings, |cpu_id| {
[Some(48), None, None][cpu_id]
}),
Err(TargetCpuCapabilityError::Missing {
capability: "IPA bits",
cpu_id: 2,
})
);
}
}