use alloc::{string::String, vec::Vec};
pub use axvm_types::{
AddressSpacePolicy, EmulatedDeviceConfig, GuestPhysAddr, PassThroughAddressConfig,
PassThroughDeviceConfig, PassThroughPortConfig, ReservedAddressConfig, VMBootProtocol,
VMInterruptMode, VMType, VmMemConfig, VmMemMappingType,
};
use crate::arch::{ArchOps, CurrentArch};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum GuestBootPolicy {
#[default]
KeepConfigured,
AdjustKernelForBootProtocol { protocol: VMBootProtocol },
}
#[derive(Clone, Copy, Debug, Default)]
pub struct AxVCpuConfig {
pub bsp_entry: GuestPhysAddr,
pub ap_entry: GuestPhysAddr,
}
#[derive(Debug, Default, Clone)]
pub struct RamdiskInfo {
pub load_gpa: GuestPhysAddr,
pub size: Option<usize>,
}
#[derive(Debug, Default, Clone)]
pub struct VMImageConfig {
pub kernel_load_gpa: GuestPhysAddr,
pub loaded_from_filesystem: bool,
pub bios_load_gpa: Option<GuestPhysAddr>,
pub dtb_load_gpa: Option<GuestPhysAddr>,
pub ramdisk: Option<RamdiskInfo>,
}
#[derive(Debug, Default)]
pub struct AxVMConfig {
id: usize,
name: String,
#[allow(dead_code)]
vm_type: VMType,
pub(crate) phys_cpu_ls: PhysCpuList,
pub cpu_config: AxVCpuConfig,
pub image_config: VMImageConfig,
emu_devices: Vec<EmulatedDeviceConfig>,
pass_through_devices: Vec<PassThroughDeviceConfig>,
excluded_devices: Vec<Vec<String>>,
pass_through_addresses: Vec<PassThroughAddressConfig>,
reserved_address_ranges: Vec<ReservedAddressConfig>,
pass_through_ports: Vec<PassThroughPortConfig>,
address_space_policy: AddressSpacePolicy,
memory_regions: Vec<VmMemConfig>,
boot_policy: GuestBootPolicy,
spi_list: Vec<u32>,
interrupt_mode: VMInterruptMode,
}
#[derive(Debug, Default)]
pub struct AxVMConfigParams {
pub id: usize,
pub name: String,
pub vm_type: VMType,
pub phys_cpu_ls: PhysCpuList,
pub cpu_config: AxVCpuConfig,
pub image_config: VMImageConfig,
pub emu_devices: Vec<EmulatedDeviceConfig>,
pub pass_through_devices: Vec<PassThroughDeviceConfig>,
pub excluded_devices: Vec<Vec<String>>,
pub pass_through_addresses: Vec<PassThroughAddressConfig>,
pub reserved_address_ranges: Vec<ReservedAddressConfig>,
pub pass_through_ports: Vec<PassThroughPortConfig>,
pub address_space_policy: AddressSpacePolicy,
pub memory_regions: Vec<VmMemConfig>,
pub boot_policy: GuestBootPolicy,
pub interrupt_mode: VMInterruptMode,
}
impl AxVMConfig {
pub fn new(params: AxVMConfigParams) -> Self {
Self {
id: params.id,
name: params.name,
vm_type: params.vm_type,
phys_cpu_ls: params.phys_cpu_ls,
cpu_config: params.cpu_config,
image_config: params.image_config,
emu_devices: params.emu_devices,
pass_through_devices: params.pass_through_devices,
excluded_devices: params.excluded_devices,
pass_through_addresses: params.pass_through_addresses,
reserved_address_ranges: params.reserved_address_ranges,
pass_through_ports: params.pass_through_ports,
address_space_policy: params.address_space_policy,
memory_regions: params.memory_regions,
boot_policy: params.boot_policy,
spi_list: Vec::new(),
interrupt_mode: params.interrupt_mode,
}
}
#[cfg(test)]
pub(crate) fn default_for_test(id: usize, name: &str) -> Self {
Self::new(AxVMConfigParams {
id,
name: String::from(name),
phys_cpu_ls: PhysCpuList::new(1, None, None),
..Default::default()
})
}
pub fn id(&self) -> usize {
self.id
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn image_config(&self) -> &VMImageConfig {
&self.image_config
}
pub fn clear_dtb_load_gpa(&mut self) {
self.image_config.dtb_load_gpa = None;
}
pub fn set_dtb_load_gpa(&mut self, dtb_load_gpa: GuestPhysAddr) {
self.image_config.dtb_load_gpa = Some(dtb_load_gpa);
}
pub fn images_loaded_from_filesystem(&self) -> bool {
self.image_config.loaded_from_filesystem
}
pub fn bsp_entry(&self) -> GuestPhysAddr {
self.cpu_config.bsp_entry
}
pub fn ap_entry(&self) -> GuestPhysAddr {
self.cpu_config.ap_entry
}
pub fn phys_cpu_ls_mut(&mut self) -> &mut PhysCpuList {
&mut self.phys_cpu_ls
}
pub fn excluded_devices(&self) -> &Vec<Vec<String>> {
&self.excluded_devices
}
pub fn pass_through_addresses(&self) -> &Vec<PassThroughAddressConfig> {
&self.pass_through_addresses
}
pub fn reserved_address_ranges(&self) -> &Vec<ReservedAddressConfig> {
&self.reserved_address_ranges
}
pub fn add_reserved_address_range(&mut self, range: ReservedAddressConfig) {
self.reserved_address_ranges.push(range);
}
pub fn pass_through_ports(&self) -> &Vec<PassThroughPortConfig> {
&self.pass_through_ports
}
pub fn address_space_policy(&self) -> AddressSpacePolicy {
self.address_space_policy
}
pub fn memory_regions(&self) -> &[VmMemConfig] {
&self.memory_regions
}
pub fn set_memory_regions(&mut self, memory_regions: Vec<VmMemConfig>) {
self.memory_regions = memory_regions;
}
pub fn boot_policy(&self) -> GuestBootPolicy {
self.boot_policy
}
pub fn set_boot_policy(&mut self, boot_policy: GuestBootPolicy) {
self.boot_policy = boot_policy;
}
pub fn emu_devices(&self) -> &Vec<EmulatedDeviceConfig> {
&self.emu_devices
}
pub fn pass_through_devices(&self) -> &Vec<PassThroughDeviceConfig> {
&self.pass_through_devices
}
pub fn add_pass_through_device(&mut self, device: PassThroughDeviceConfig) {
self.pass_through_devices.push(device);
}
pub fn remove_pass_through_device(&mut self, device: PassThroughDeviceConfig) {
self.pass_through_devices.retain(|d| d != &device);
}
pub fn clear_pass_through_devices(&mut self) {
self.pass_through_devices.clear();
}
pub fn add_pass_through_spi(&mut self, spi: u32) {
self.spi_list.push(spi);
}
pub fn pass_through_spis(&self) -> &Vec<u32> {
&self.spi_list
}
pub fn interrupt_mode(&self) -> VMInterruptMode {
self.interrupt_mode
}
pub fn relocate_kernel_image(&mut self, kernel_load_gpa: GuestPhysAddr) {
let old_load = self.image_config.kernel_load_gpa.as_usize();
let new_load = kernel_load_gpa.as_usize();
let bsp_offset = self
.cpu_config
.bsp_entry
.as_usize()
.checked_sub(old_load)
.expect("BSP entry must not be below kernel load address");
let ap_offset = self
.cpu_config
.ap_entry
.as_usize()
.checked_sub(old_load)
.expect("AP entry must not be below kernel load address");
self.image_config.kernel_load_gpa = kernel_load_gpa;
self.cpu_config.bsp_entry = GuestPhysAddr::from(new_load + bsp_offset);
self.cpu_config.ap_entry = GuestPhysAddr::from(new_load + ap_offset);
}
}
#[derive(Debug, Default, Clone)]
pub struct PhysCpuList {
cpu_num: usize,
phys_cpu_ids: Option<Vec<usize>>,
phys_cpu_sets: Option<Vec<usize>>,
}
impl PhysCpuList {
pub fn new(
cpu_num: usize,
phys_cpu_ids: Option<Vec<usize>>,
phys_cpu_sets: Option<Vec<usize>>,
) -> Self {
Self {
cpu_num,
phys_cpu_ids,
phys_cpu_sets,
}
}
pub fn get_vcpu_affinities_pcpu_ids(&self) -> Vec<(usize, Option<usize>, usize)> {
if let Some(phys_cpu_ids) = &self.phys_cpu_ids
&& self.cpu_num != phys_cpu_ids.len()
{
error!(
"ERROR!!!: cpu_num: {}, phys_cpu_ids: {:?}",
self.cpu_num, self.phys_cpu_ids
);
}
CurrentArch::vcpu_affinities(
self.cpu_num,
self.phys_cpu_ids.as_deref(),
self.phys_cpu_sets.as_deref(),
)
}
pub fn cpu_num(&self) -> usize {
self.cpu_num
}
pub fn phys_cpu_ids(&self) -> &Option<Vec<usize>> {
&self.phys_cpu_ids
}
pub fn phys_cpu_sets(&self) -> &Option<Vec<usize>> {
&self.phys_cpu_sets
}
pub fn set_guest_cpu_sets(&mut self, phys_cpu_sets: Vec<usize>) {
self.phys_cpu_sets = Some(phys_cpu_sets);
}
pub fn set_guest_phys_cpu_ids(&mut self, phys_cpu_ids: Vec<usize>) {
self.phys_cpu_ids = Some(phys_cpu_ids);
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use super::*;
fn memory_region(gpa: usize, size: usize, map_type: VmMemMappingType) -> VmMemConfig {
VmMemConfig {
gpa,
size,
flags: 0x7,
map_type,
}
}
#[test]
fn set_memory_regions_replaces_stale_snapshot_after_config_enrichment() {
let main_memory = memory_region(0x8000_0000, 0x200000, VmMemMappingType::MapIdentical);
let reserved_memory = memory_region(0x110000, 0x10000, VmMemMappingType::MapReserved);
let mut config = AxVMConfig::default_for_test(1, "linux");
config.set_memory_regions(vec![main_memory.clone()]);
assert_eq!(config.memory_regions().len(), 1);
config.set_memory_regions(vec![main_memory, reserved_memory]);
let regions = config.memory_regions();
assert_eq!(regions.len(), 2);
assert_eq!(regions[1].gpa, 0x110000);
assert_eq!(regions[1].size, 0x10000);
assert_eq!(regions[1].map_type, VmMemMappingType::MapReserved);
}
}