Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 12 additions & 86 deletions os/axvisor/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,19 @@ use core::sync::atomic::{AtomicBool, Ordering};
use ax_errno::{AxResult, ax_err_type};
#[cfg(all(feature = "fs", target_arch = "x86_64"))]
use axvm::InterruptTriggerMode;
#[cfg(target_arch = "x86_64")]
use axvm::config::VMBootProtocol;
use axvm::{
AxVM, GuestPhysAddr,
boot::{BootImageProvider, ImageLoader, StaticVmImage, get_image_header},
boot::{
BootImageProvider, StaticVmImage, boot_firmware_load_gpa, get_image_header,
guest_boot_policy, init_guest_boot_resources, prepare_guest_boot,
},
config::{
AxVCpuConfig, AxVMConfig, AxVMConfigParams, GuestBootPolicy, PhysCpuList, RamdiskInfo,
VMImageConfig,
},
};
use axvmconfig::{AxVMCrateConfig, VMType};

#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use axvm::boot::handle_fdt_operations;
#[cfg(target_arch = "x86_64")]
use axvm::boot::is_x86_linux_image_config;
#[cfg(target_arch = "loongarch64")]
use axvm::boot::{handle_fdt_operations, init_guest_boot_resources};

/// Default BIOS load GPA for x86_64 built-in BIOS.
#[cfg(target_arch = "x86_64")]
const DEFAULT_X86_BIOS_LOAD_GPA: usize = 0x8000;

#[cfg(all(
feature = "fs",
any(target_arch = "x86_64", target_arch = "loongarch64")
Expand Down Expand Up @@ -88,11 +78,7 @@ pub mod vmcfg {
}

pub fn init_guest_vms() {
// Initialize LoongArch firmware resources before guest configs are materialized.
#[cfg(target_arch = "loongarch64")]
{
init_guest_boot_resources();
}
init_guest_boot_resources();

// First try to get configs from filesystem if fs feature is enabled
let mut gvm_raw_configs = vmcfg::filesystem_vm_configs();
Expand Down Expand Up @@ -120,8 +106,7 @@ pub fn init_guest_vms() {

pub fn init_guest_vm(raw_cfg: &str) -> AxResult<usize> {
let image_provider = AxvisorBootImageProvider;
#[allow(unused_mut)]
let mut vm_create_config = AxVMCrateConfig::from_toml(raw_cfg)
let vm_create_config = AxVMCrateConfig::from_toml(raw_cfg)
.map_err(|e| ax_err_type!(InvalidData, format!("Failed to resolve VM config: {e:?}")))?;

#[cfg(all(
Expand All @@ -137,25 +122,13 @@ pub fn init_guest_vm(raw_cfg: &str) -> AxResult<usize> {
);
}

#[allow(unused_mut)]
let mut vm_config = build_axvm_config(&vm_create_config);
let prepared_boot = prepare_guest_boot(&mut vm_config, vm_create_config, &image_provider)?;
let prepared_config = prepared_boot.config();

// Handle FDT-related operations for architectures that boot guests with DTB.
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
let guest_dtb = handle_fdt_operations(&mut vm_config, &mut vm_create_config, &image_provider)?;
#[cfg(target_arch = "loongarch64")]
handle_fdt_operations(&mut vm_config, &mut vm_create_config)?;
sync_axvm_config_from_crate_config(&mut vm_config, prepared_config);

sync_axvm_config_from_crate_config(&mut vm_config, &vm_create_config);

#[cfg(target_arch = "x86_64")]
let skip_guest_address_adjustment = x86_linux_direct_boot_config(&vm_create_config);
#[cfg(not(target_arch = "x86_64"))]
let skip_guest_address_adjustment = false;
vm_config.set_boot_policy(guest_boot_policy(
&vm_create_config,
skip_guest_address_adjustment,
));
vm_config.set_boot_policy(guest_boot_policy(prepared_config, &image_provider));

// info!("after parse_vm_interrupt, crate VM[{}] with config: {:#?}", vm_config.id(), vm_config);
info!("Creating VM[{}] {:?}", vm_config.id(), vm_config.name());
Expand All @@ -171,17 +144,7 @@ pub fn init_guest_vm(raw_cfg: &str) -> AxResult<usize> {
// Load corresponding images for VM.
info!("VM[{}] created success, loading images...", vm.id());

#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
let mut loader = ImageLoader::new(
main_mem,
vm_create_config,
vm.clone(),
&image_provider,
guest_dtb,
);
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
let mut loader = ImageLoader::new(main_mem, vm_create_config, vm.clone(), &image_provider);
loader.load()?;
prepared_boot.load_images(main_mem, vm.clone(), &image_provider)?;

vm.prepare()
.map_err(|e| ax_err_type!(InvalidData, format!("VM[{}] setup failed: {e:?}", vm.id())))?;
Expand Down Expand Up @@ -225,7 +188,7 @@ pub(crate) fn build_axvm_config(cfg: &AxVMCrateConfig) -> AxVMConfig {
image_config: VMImageConfig {
kernel_load_gpa: GuestPhysAddr::from(cfg.kernel.kernel_load_addr),
loaded_from_filesystem: cfg.kernel.image_location.as_deref() == Some("fs"),
bios_load_gpa: configured_bios_load_gpa(cfg),
bios_load_gpa: boot_firmware_load_gpa(cfg),
dtb_load_gpa: cfg.kernel.dtb_load_addr.map(GuestPhysAddr::from),
ramdisk: cfg.kernel.ramdisk_load_addr.map(|addr| RamdiskInfo {
load_gpa: GuestPhysAddr::from(addr),
Expand All @@ -249,38 +212,6 @@ fn sync_axvm_config_from_crate_config(vm_config: &mut AxVMConfig, cfg: &AxVMCrat
vm_config.set_memory_regions(cfg.kernel.memory_regions.clone());
}

fn guest_boot_policy(
cfg: &AxVMCrateConfig,
skip_guest_address_adjustment: bool,
) -> GuestBootPolicy {
if skip_guest_address_adjustment {
GuestBootPolicy::KeepConfigured
} else {
GuestBootPolicy::AdjustKernelForBootProtocol {
protocol: cfg.kernel.effective_boot_protocol(),
}
}
}

fn configured_bios_load_gpa(cfg: &AxVMCrateConfig) -> Option<GuestPhysAddr> {
if !cfg.kernel.enable_bios {
return None;
}

if let Some(addr) = cfg.kernel.bios_load_addr {
return Some(GuestPhysAddr::from(addr));
}

#[cfg(target_arch = "x86_64")]
if cfg.kernel.boot_firmware_path().is_none()
&& cfg.kernel.effective_boot_protocol() == VMBootProtocol::Multiboot
{
return Some(GuestPhysAddr::from(DEFAULT_X86_BIOS_LOAD_GPA));
}

None
}

#[cfg(all(
feature = "fs",
any(target_arch = "x86_64", target_arch = "loongarch64")
Expand Down Expand Up @@ -419,11 +350,6 @@ fn x86_intx_forwarding_trigger(binding: &ax_driver::BindingIrq) -> InterruptTrig
}
}

#[cfg(target_arch = "x86_64")]
fn x86_linux_direct_boot_config(config: &AxVMCrateConfig) -> bool {
is_x86_linux_image_config(config, &AxvisorBootImageProvider)
}

struct AxvisorBootImageProvider;

impl BootImageProvider for AxvisorBootImageProvider {
Expand Down
7 changes: 2 additions & 5 deletions os/axvisor/src/shell/command/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ fn can_start_vm(status: VmStatus) -> Result<(), &'static str> {
VmStatus::Running => Err("VM is already running"),
VmStatus::Paused => Err("VM is suspended, use 'vm resume' instead"),
VmStatus::Stopping => Err("VM is stopping, wait for it to fully stop"),
VmStatus::Uninit => Err("VM is still loading"),
VmStatus::Pausing => Err("VM is pausing"),
VmStatus::Destroying | VmStatus::Destroyed => Err("VM is being destroyed"),
VmStatus::Failed => Err("VM is failed"),
Expand All @@ -54,7 +53,7 @@ fn can_stop_vm(status: VmStatus, force: bool) -> Result<(), &'static str> {
}
}
VmStatus::Stopped => Err("VM is already stopped"),
VmStatus::Uninit | VmStatus::Ready => Ok(()), // Allow stopping VMs in these states
VmStatus::Ready => Ok(()), // Allow stopping VMs before their first start.
VmStatus::Pausing => Err("VM is pausing"),
VmStatus::Destroying | VmStatus::Destroyed => Err("VM is being destroyed"),
VmStatus::Failed => Err("VM is failed"),
Expand All @@ -68,7 +67,6 @@ fn can_suspend_vm(status: VmStatus) -> Result<(), &'static str> {
VmStatus::Paused => Err("VM is already suspended"),
VmStatus::Stopped => Err("VM is stopped, cannot suspend"),
VmStatus::Stopping => Err("VM is stopping, cannot suspend"),
VmStatus::Uninit => Err("VM is loading, cannot suspend"),
VmStatus::Ready => Err("VM is not running, cannot suspend"),
VmStatus::Pausing => Err("VM is already pausing"),
VmStatus::Destroying | VmStatus::Destroyed => Err("VM is being destroyed"),
Expand All @@ -83,7 +81,6 @@ fn can_resume_vm(status: VmStatus) -> Result<(), &'static str> {
VmStatus::Running => Err("VM is already running"),
VmStatus::Stopped => Err("VM is stopped, use 'vm start' instead"),
VmStatus::Stopping => Err("VM is stopping, cannot resume"),
VmStatus::Uninit => Err("VM is loading, cannot resume"),
VmStatus::Ready => Err("VM is not started yet, use 'vm start' instead"),
VmStatus::Pausing => Err("VM is pausing, wait before resuming"),
VmStatus::Destroying | VmStatus::Destroyed => Err("VM is being destroyed"),
Expand Down Expand Up @@ -305,7 +302,7 @@ fn stop_vm_by_id(vm_id: usize, force: bool) {
println!("Gracefully stopping VM[{}]...", vm_id);
}
}
VmStatus::Uninit | VmStatus::Ready => {
VmStatus::Ready => {
println!(
"⚠ VM[{}] is in {:?} state, stopping anyway...",
vm_id, status
Expand Down
86 changes: 86 additions & 0 deletions virtualization/axvm/src/arch/aarch64/capabilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! AArch64 implementations of AxVM platform capability hooks.

use alloc::format;

use ax_errno::{AxResult, ax_err_type};

use super::Aarch64Arch;
use crate::architecture::{BootImagePlatform, GuestBootPlatform, HostTimePlatform};

impl HostTimePlatform for Aarch64Arch {}

impl BootImagePlatform for Aarch64Arch {
fn load_guest_dtb(
loader: &crate::boot::images::ImageLoaderCore<'_>,
dtb: &crate::boot::fdt::GuestDtbImage,
) -> AxResult {
let bytes = dtb.as_bytes();
let source = core::ptr::NonNull::new(bytes.as_ptr() as *mut u8)
.ok_or_else(|| ax_err_type!(InvalidData, "Guest DTB pointer is null"))?;
super::fdt::core::update_fdt(source, bytes.len(), loader.vm.clone(), &loader.config)
}
}

impl GuestBootPlatform for Aarch64Arch {
fn prepare_guest_boot(
vm_config: &mut crate::config::AxVMConfig,
vm_create_config: &mut axvmconfig::AxVMCrateConfig,
provider: &dyn crate::boot::BootImageProvider,
) -> AxResult<Option<crate::boot::fdt::GuestDtbImage>> {
super::fdt::core::prepare_dtb_guest(vm_config, vm_create_config, provider)
}
}

pub fn host_fdt_bootarg() -> usize {
ax_std::os::arceos::modules::ax_hal::dtb::get_bootarg()
}

pub fn host_phys_to_virt(paddr: ax_memory_addr::PhysAddr) -> ax_memory_addr::VirtAddr {
ax_std::os::arceos::modules::ax_hal::mem::phys_to_virt(paddr)
}

pub(super) fn decode_gic_spi(specifier: &[u32]) -> Option<u32> {
(specifier.first().copied() == Some(0))
.then(|| specifier.get(1).copied())
.flatten()
}

pub(super) fn patch_runtime_fdt(
fdt_bytes: &[u8],
vm: &crate::AxVMRef,
crate_config: &axvmconfig::AxVMCrateConfig,
) -> AxResult<alloc::vec::Vec<u8>> {
let initrd = vm.with_config(|config| {
super::fdt::initrd_start_size_from_image_config(config.image_config.ramdisk.as_ref())
});
super::fdt::core::patch_guest_fdt_for_runtime(
fdt_bytes,
&vm.memory_regions(),
crate_config,
initrd,
true,
)
}

pub(super) fn patch_provided_fdt(
provided_dtb: &[u8],
host_dtb: Option<&[u8]>,
crate_config: &axvmconfig::AxVMCrateConfig,
) -> AxResult<alloc::vec::Vec<u8>> {
let provided_fdt = fdt_edit::Fdt::from_bytes(provided_dtb).map_err(|err| {
ax_err_type!(
InvalidData,
format!("Failed to parse provided DTB image: {err:#?}")
)
})?;
let host_fdt = host_dtb
.map(fdt_edit::Fdt::from_bytes)
.transpose()
.map_err(|err| {
ax_err_type!(
InvalidData,
format!("Failed to parse host DTB image: {err:#?}")
)
})?;
super::fdt::update_cpu_node(&provided_fdt, host_fdt.as_ref(), crate_config)
}
97 changes: 97 additions & 0 deletions virtualization/axvm/src/arch/aarch64/fdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! AArch64 compatibility facade and target-specific guest FDT policy.

use alloc::vec::Vec;

use ax_errno::{AxResult, ax_err_type};
use fdt_edit::Fdt;

use crate::{
boot::{BootImageProvider, fdt::GuestDtbImage},
config::AxVMConfig,
};

#[path = "../../boot/fdt/core/mod.rs"]
pub(crate) mod core;

pub use core::{
parse_passthrough_devices_address, parse_reserved_memory_regions, parse_vm_interrupt,
reserve_excluded_device_ranges, set_phys_cpu_sets, setup_guest_fdt_from_vmm, try_get_host_fdt,
update_fdt, update_provided_fdt,
};

pub(crate) fn guest_fdt_policy() -> core::GuestFdtPolicy {
core::GuestFdtPolicy {
patch_runtime: super::capabilities::patch_runtime_fdt,
patch_provided: super::capabilities::patch_provided_fdt,
decode_interrupt: super::capabilities::decode_gic_spi,
}
}

pub(crate) fn host_fdt_bootarg() -> usize {
super::capabilities::host_fdt_bootarg()
}

pub(crate) fn host_phys_to_virt(paddr: ax_memory_addr::PhysAddr) -> ax_memory_addr::VirtAddr {
super::capabilities::host_phys_to_virt(paddr)
}

pub(super) fn initrd_start_size_from_image_config(
ramdisk: Option<&crate::config::RamdiskInfo>,
) -> Option<(u64, u64)> {
let ramdisk = ramdisk?;
Some((ramdisk.load_gpa.as_usize() as u64, ramdisk.size? as u64))
}

pub(super) fn update_cpu_node(
fdt: &Fdt,
host_fdt: Option<&Fdt>,
crate_config: &axvmconfig::AxVMCrateConfig,
) -> AxResult<Vec<u8>> {
let Some(host_fdt) = host_fdt else {
return Ok(fdt.encode().as_ref().to_vec());
};

let phys_cpu_ids = crate_config
.base
.phys_cpu_ids
.as_deref()
.ok_or_else(|| ax_err_type!(InvalidInput, "phys_cpu_ids is missing"))?;
let mut tree = core::tree::FdtTree::from_fdt(fdt.clone());
tree.inner_mut().remove_by_path("/cpus");

if let Some(host_cpus_id) = host_fdt.get_by_path_id("/cpus") {
let cpus_id =
tree.copy_subtree_from(host_fdt, host_cpus_id, tree.inner().root_id(), true)?;
let cpu_paths = tree
.node_paths()
.into_iter()
.filter_map(|(id, path)| {
(path.starts_with("/cpus/cpu@")
&& !core::create::need_cpu_node(phys_cpu_ids, tree.inner(), id, &path))
.then_some(path)
})
.collect::<Vec<_>>();
for path in cpu_paths {
tree.inner_mut().remove_by_path(&path);
}
if let Some(cpus) = tree.inner_mut().node_mut(cpus_id) {
for property in [
"riscv,cbop-block-size",
"riscv,cboz-block-size",
"riscv,cbom-block-size",
] {
cpus.remove_property(property);
}
}
}

Ok(tree.finish())
}

pub fn handle_fdt_operations(
vm_config: &mut AxVMConfig,
vm_create_config: &mut axvmconfig::AxVMCrateConfig,
provider: &dyn BootImageProvider,
) -> AxResult<Option<GuestDtbImage>> {
core::prepare_dtb_guest(vm_config, vm_create_config, provider)
}
Loading