Skip to content
Merged
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