Skip to content

Builder refactor #5112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 26, 2025
3 changes: 2 additions & 1 deletion src/vmm/src/arch/aarch64/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use std::fmt::Debug;
use vm_fdt::{Error as VmFdtError, FdtWriter, FdtWriterNode};
use vm_memory::GuestMemoryError;

use super::super::{DeviceType, InitrdConfig};
use super::super::DeviceType;
use super::cache_info::{CacheEntry, read_cache_config};
use super::gic::GICDevice;
use crate::device_manager::mmio::MMIODeviceInfo;
use crate::devices::acpi::vmgenid::{VMGENID_MEM_SIZE, VmGenId};
use crate::initrd::InitrdConfig;
use crate::vstate::memory::{Address, GuestMemory, GuestMemoryMmap};

// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
Expand Down
148 changes: 101 additions & 47 deletions src/vmm/src/arch/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,38 @@
pub mod vm;

use std::cmp::min;
use std::collections::HashMap;
use std::ffi::CString;
use std::fmt::Debug;
use std::fs::File;

use linux_loader::loader::pe::PE as Loader;
use linux_loader::loader::{Cmdline, KernelLoader};
use vm_memory::GuestMemoryError;

use self::gic::GICDevice;
use crate::arch::DeviceType;
use crate::device_manager::mmio::MMIODeviceInfo;
use crate::devices::acpi::vmgenid::VmGenId;
use crate::arch::{BootProtocol, EntryPoint};
use crate::cpu_config::aarch64::{CpuConfiguration, CpuConfigurationError};
use crate::cpu_config::templates::CustomCpuTemplate;
use crate::initrd::InitrdConfig;
use crate::utils::{align_up, usize_to_u64};
use crate::vmm_config::machine_config::MachineConfig;
use crate::vstate::memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap};
use crate::vstate::vcpu::KvmVcpuError;
use crate::{Vcpu, VcpuConfig, Vmm};

/// Errors thrown while configuring aarch64 system.
#[derive(Debug, thiserror::Error, displaydoc::Display)]
pub enum ConfigurationError {
/// Failed to create a Flattened Device Tree for this aarch64 microVM: {0}
SetupFDT(#[from] fdt::FdtError),
/// Failed to compute the initrd address.
InitrdAddress,
/// Failed to write to guest memory.
MemoryError(GuestMemoryError),
MemoryError(#[from] GuestMemoryError),
/// Cannot copy kernel file fd
KernelFile,
/// Cannot load kernel due to invalid memory configuration or invalid kernel image: {0}
KernelLoader(#[from] linux_loader::loader::Error),
/// Error creating vcpu configuration: {0}
VcpuConfig(#[from] CpuConfigurationError),
/// Error configuring the vcpu: {0}
VcpuConfigure(#[from] KvmVcpuError),
}

/// The start of the memory area reserved for MMIO devices.
Expand All @@ -52,39 +63,59 @@
vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)]
}

/// Configures the system and should be called once per vm before starting vcpu threads.
/// For aarch64, we only setup the FDT.
///
/// # Arguments
///
/// * `guest_mem` - The memory to be used by the guest.
/// * `cmdline_cstring` - The kernel commandline.
/// * `vcpu_mpidr` - Array of MPIDR register values per vcpu.
/// * `device_info` - A hashmap containing the attached devices for building FDT device nodes.
/// * `gic_device` - The GIC device.
/// * `initrd` - Information about an optional initrd.
pub fn configure_system(
guest_mem: &GuestMemoryMmap,
cmdline_cstring: CString,
vcpu_mpidr: Vec<u64>,
device_info: &HashMap<(DeviceType, String), MMIODeviceInfo>,
gic_device: &GICDevice,
vmgenid: &Option<VmGenId>,
initrd: &Option<super::InitrdConfig>,
/// Configures the system for booting Linux.
pub fn configure_system_for_boot(
vmm: &mut Vmm,
vcpus: &mut [Vcpu],
machine_config: &MachineConfig,
cpu_template: &CustomCpuTemplate,
entry_point: EntryPoint,
initrd: &Option<InitrdConfig>,
boot_cmdline: Cmdline,
) -> Result<(), ConfigurationError> {
// Construct the base CpuConfiguration to apply CPU template onto.
let cpu_config = CpuConfiguration::new(cpu_template, vcpus)?;

// Apply CPU template to the base CpuConfiguration.
let cpu_config = CpuConfiguration::apply_template(cpu_config, cpu_template);

let vcpu_config = VcpuConfig {
vcpu_count: machine_config.vcpu_count,
smt: machine_config.smt,
cpu_config,
};

let optional_capabilities = vmm.kvm.optional_capabilities();
// Configure vCPUs with normalizing and setting the generated CPU configuration.
for vcpu in vcpus.iter_mut() {
vcpu.kvm_vcpu.configure(
&vmm.guest_memory,
entry_point,
&vcpu_config,
&optional_capabilities,
)?;
}
let vcpu_mpidr = vcpus
.iter_mut()
.map(|cpu| cpu.kvm_vcpu.get_mpidr())
.collect();
let cmdline = boot_cmdline
.as_cstring()
.expect("Cannot create cstring from cmdline string");

let fdt = fdt::create_fdt(
guest_mem,
&vmm.guest_memory,
vcpu_mpidr,
cmdline_cstring,
device_info,
gic_device,
vmgenid,
cmdline,
vmm.mmio_device_manager.get_device_info(),
vmm.vm.get_irqchip(),
&vmm.acpi_device_manager.vmgenid,
initrd,
)?;
let fdt_address = GuestAddress(get_fdt_addr(guest_mem));
guest_mem
.write_slice(fdt.as_slice(), fdt_address)
.map_err(ConfigurationError::MemoryError)?;

let fdt_address = GuestAddress(get_fdt_addr(&vmm.guest_memory));
vmm.guest_memory.write_slice(fdt.as_slice(), fdt_address)?;

Ok(())
}

Expand All @@ -94,21 +125,20 @@
}

/// Returns the memory address where the initrd could be loaded.
pub fn initrd_load_addr(
guest_mem: &GuestMemoryMmap,
initrd_size: usize,
) -> Result<u64, ConfigurationError> {
let round_to_pagesize =
|size| (size + (super::GUEST_PAGE_SIZE - 1)) & !(super::GUEST_PAGE_SIZE - 1);
match GuestAddress(get_fdt_addr(guest_mem)).checked_sub(round_to_pagesize(initrd_size) as u64) {
pub fn initrd_load_addr(guest_mem: &GuestMemoryMmap, initrd_size: usize) -> Option<u64> {
let rounded_size = align_up(
usize_to_u64(initrd_size),
usize_to_u64(super::GUEST_PAGE_SIZE),
);
match GuestAddress(get_fdt_addr(guest_mem)).checked_sub(rounded_size) {
Some(offset) => {
if guest_mem.address_in_range(offset) {
Ok(offset.raw_value())
Some(offset.raw_value())
} else {
Err(ConfigurationError::InitrdAddress)
None
}
}
None => Err(ConfigurationError::InitrdAddress),
None => None,

Check warning on line 141 in src/vmm/src/arch/aarch64/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/arch/aarch64/mod.rs#L141

Added line #L141 was not covered by tests
}
}

Expand All @@ -127,6 +157,30 @@
layout::DRAM_MEM_START
}

/// Load linux kernel into guest memory.
pub fn load_kernel(
kernel: &File,
guest_memory: &GuestMemoryMmap,
) -> Result<EntryPoint, ConfigurationError> {
// Need to clone the File because reading from it
// mutates it.
let mut kernel_file = kernel
.try_clone()
.map_err(|_| ConfigurationError::KernelFile)?;

let entry_addr = Loader::load(
guest_memory,
Some(GuestAddress(get_kernel_start())),
&mut kernel_file,
None,
)?;

Ok(EntryPoint {
entry_addr: entry_addr.kernel_load,
protocol: BootProtocol::LinuxBoot,
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
27 changes: 10 additions & 17 deletions src/vmm/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ pub use aarch64::vcpu::*;
pub use aarch64::vm::{ArchVm, ArchVmError, VmState};
#[cfg(target_arch = "aarch64")]
pub use aarch64::{
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions, configure_system,
get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE,
layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START,
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions,
configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE,
layout::IRQ_BASE, layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START,
load_kernel,
};

/// Module for x86_64 related functionality.
Expand All @@ -35,12 +36,13 @@ pub use x86_64::kvm::{Kvm, KvmArchError};
pub use x86_64::vcpu::*;
#[cfg(target_arch = "x86_64")]
pub use x86_64::vm::{ArchVm, ArchVmError, VmState};

#[cfg(target_arch = "x86_64")]
pub use x86_64::{
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions, configure_system,
get_kernel_start, initrd_load_addr, layout::APIC_ADDR, layout::CMDLINE_MAX_SIZE,
layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX, layout::SYSTEM_MEM_SIZE,
layout::SYSTEM_MEM_START,
pub use crate::arch::x86_64::{
ConfigurationError, MMIO_MEM_SIZE, MMIO_MEM_START, arch_memory_regions,
configure_system_for_boot, get_kernel_start, initrd_load_addr, layout::APIC_ADDR,
layout::CMDLINE_MAX_SIZE, layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX,
layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
};

/// Types of devices that can get attached to this platform.
Expand All @@ -58,15 +60,6 @@ pub enum DeviceType {
BootTimer,
}

/// Type for passing information about the initrd in the guest memory.
#[derive(Debug)]
pub struct InitrdConfig {
/// Load address of initrd in guest memory
pub address: crate::vstate::memory::GuestAddress,
/// Size of initrd in guest memory
pub size: usize,
}

/// Default page size for the guest OS.
pub const GUEST_PAGE_SIZE: usize = 4096;

Expand Down
Loading