use alloc::{string::String, vec::Vec};
pub use axvm_types::{
EmulatedDeviceConfig, GuestPhysAddr, PassThroughAddressConfig, PassThroughDeviceConfig,
VMBootProtocol, VMInterruptMode, VMType, VmMemConfig, VmMemMappingType,
};
use crate::VMMemoryRegion;
const BIOS_RESERVED_SIZE: usize = 2 * 1024 * 1024;
#[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>,
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 interrupt_mode: VMInterruptMode,
}
pub fn adjusted_kernel_load_gpa(
main_memory: &VMMemoryRegion,
boot_protocol: VMBootProtocol,
bios_load_gpa: Option<GuestPhysAddr>,
) -> Option<GuestPhysAddr> {
if !main_memory.is_identical() {
return None;
}
let mut kernel_addr = main_memory.gpa;
if boot_protocol == VMBootProtocol::Multiboot && bios_load_gpa.is_some() {
kernel_addr += BIOS_RESERVED_SIZE;
}
Some(kernel_addr)
}
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,
spi_list: Vec::new(),
interrupt_mode: params.interrupt_mode,
}
}
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 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 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)> {
let mut vcpu_pcpu_tuples = Vec::new();
#[cfg(target_arch = "riscv64")]
let mut pcpu_mask_flag = false;
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
);
}
for vcpu_id in 0..self.cpu_num {
vcpu_pcpu_tuples.push((vcpu_id, None, vcpu_id));
}
#[cfg(target_arch = "riscv64")]
if let Some(phys_cpu_sets) = &self.phys_cpu_sets {
pcpu_mask_flag = true;
for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() {
vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap);
}
}
#[cfg(not(target_arch = "riscv64"))]
if let Some(phys_cpu_sets) = &self.phys_cpu_sets {
for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() {
vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap);
}
}
if let Some(phys_cpu_ids) = &self.phys_cpu_ids {
for (vcpu_id, phys_id) in phys_cpu_ids.iter().enumerate() {
vcpu_pcpu_tuples[vcpu_id].2 = *phys_id;
#[cfg(target_arch = "riscv64")]
{
if !pcpu_mask_flag {
vcpu_pcpu_tuples[vcpu_id].1 = Some(1 << (*phys_id));
}
}
}
}
vcpu_pcpu_tuples
}
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);
}
}