Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
307e77b
refactor(test): add helper function for creating mem in vhost tests
roypat Mar 27, 2025
134e22a
refactor: Have all memory creation method take similar arguments
roypat Jan 31, 2025
a7755b1
refactor: introduce struct VmCommon
roypat Mar 21, 2025
fb546a6
refactor: move check for not exceeding memslot limit into `struct Vm`
roypat Mar 21, 2025
3c60fd7
refactor: store GuestMemoryMmap inside `struct Vm`
roypat Mar 21, 2025
ce33334
refactor: load kernel/initrd after create_vmm_and_vcpus()
roypat Mar 21, 2025
d40c7c0
refactor: build GuestMemoryMmap objects incrementally
roypat Mar 21, 2025
4aea148
refactor: move GuestMemoryState into VmState
roypat Mar 25, 2025
4601def
refactor: move dirty bitmap functions into struct Vm
roypat Mar 26, 2025
93dbbc2
refactor: hoist post-snapshot vring re-dirtying
roypat Mar 26, 2025
88cd6bf
refactor: move snapshot_memory_to_file into struct Vm
roypat Mar 26, 2025
93a38c0
refactor: #[from]-ify CreateSnapshotError
roypat Mar 28, 2025
8817e2f
refactor: cleanup error propagation in snapshot_emory_to_file
roypat Mar 31, 2025
50b7ace
fix: print inner error in case of PersistError::QueueConstruction
roypat Mar 28, 2025
2fd6e14
add offset argument to `arch_regions`
roypat Jan 29, 2025
bbf3cd8
refactor: drop uffd parameter from create_vmm_and_vcpus
roypat Mar 25, 2025
9b23ec5
uffd: unconditionally enable UFFD_FEATURE_EVENT_REMOVE
roypat Mar 25, 2025
1fc262b
fix(test): do not drop Vm object before end of test
roypat Mar 31, 2025
8055e0a
Merge branch 'main' into guest-mem-in-vm
roypat Apr 2, 2025
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
3 changes: 1 addition & 2 deletions src/vmm/benches/memory_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0

use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use vm_memory::GuestMemory;
use vmm::resources::VmResources;
use vmm::vmm_config::machine_config::{HugePageConfig, MachineConfig};

Expand All @@ -14,7 +13,7 @@ fn bench_single_page_fault(c: &mut Criterion, configuration: VmResources) {
// Get a pointer to the first memory region (cannot do `.get_slice(GuestAddress(0),
// 1)`, because on ARM64 guest memory does not start at physical
// address 0).
let ptr = memory.iter().next().unwrap().as_ptr();
let ptr = memory.first().unwrap().as_ptr();

// fine to return both here, because ptr is not a reference into `memory` (e.g. no
// self-referential structs are happening here)
Expand Down
17 changes: 7 additions & 10 deletions src/vmm/src/acpi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ mod tests {
use crate::acpi::{AcpiError, AcpiTableWriter};
use crate::arch::x86_64::layout::{SYSTEM_MEM_SIZE, SYSTEM_MEM_START};
use crate::builder::tests::default_vmm;
use crate::test_utils::arch_mem;
use crate::device_manager::resources::ResourceAllocator;
use crate::utils::u64_to_usize;
use crate::vstate::vm::tests::setup_vm_with_memory;

struct MockSdt(Vec<u8>);

Expand All @@ -215,7 +217,7 @@ mod tests {
// A mocke Vmm object with 128MBs of memory
let mut vmm = default_vmm();
let mut writer = AcpiTableWriter {
mem: &vmm.guest_memory,
mem: vmm.vm.guest_memory(),
resource_allocator: &mut vmm.resource_allocator,
};

Expand Down Expand Up @@ -263,15 +265,10 @@ mod tests {
// change in the future.
#[test]
fn test_write_acpi_table_small_memory() {
let mut vmm = default_vmm();
vmm.guest_memory = arch_mem(
(SYSTEM_MEM_START + SYSTEM_MEM_SIZE - 4096)
.try_into()
.unwrap(),
);
let (_, vm) = setup_vm_with_memory(u64_to_usize(SYSTEM_MEM_START + SYSTEM_MEM_SIZE - 4096));
let mut writer = AcpiTableWriter {
mem: &vmm.guest_memory,
resource_allocator: &mut vmm.resource_allocator,
mem: vm.guest_memory(),
resource_allocator: &mut ResourceAllocator::new().unwrap(),
};

let mut sdt = MockSdt(vec![0; usize::try_from(SYSTEM_MEM_SIZE).unwrap()]);
Expand Down
4 changes: 0 additions & 4 deletions src/vmm/src/arch/aarch64/kvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ pub struct OptionalCapabilities {
pub struct Kvm {
/// KVM fd.
pub fd: KvmFd,
/// Maximum number of memory slots allowed by KVM.
pub max_memslots: usize,
/// Additional capabilities that were specified in cpu template.
pub kvm_cap_modifiers: Vec<KvmCapability>,
}
Expand All @@ -42,12 +40,10 @@ impl Kvm {
/// Initialize [`Kvm`] type for Aarch64 architecture
pub fn init_arch(
fd: KvmFd,
max_memslots: usize,
kvm_cap_modifiers: Vec<KvmCapability>,
) -> Result<Self, KvmArchError> {
Ok(Self {
fd,
max_memslots,
kvm_cap_modifiers,
})
}
Expand Down
87 changes: 77 additions & 10 deletions src/vmm/src/arch/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
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};
use crate::{Vcpu, VcpuConfig, Vmm, logger};

/// Errors thrown while configuring aarch64 system.
#[derive(Debug, thiserror::Error, displaydoc::Display)]
Expand All @@ -58,9 +58,35 @@

/// Returns a Vec of the valid memory addresses for aarch64.
/// See [`layout`](layout) module for a drawing of the specific memory model for this platform.
pub fn arch_memory_regions(size: usize) -> Vec<(GuestAddress, usize)> {
let dram_size = min(size, layout::DRAM_MEM_MAX_SIZE);
vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)]
///
/// The `offset` parameter specified the offset from [`layout::DRAM_MEM_START`].
pub fn arch_memory_regions(offset: usize, size: usize) -> Vec<(GuestAddress, usize)> {
assert!(size > 0, "Attempt to allocate guest memory of length 0");
assert!(
offset.checked_add(size).is_some(),
"Attempt to allocate guest memory such that the address space would wrap around"

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L67 was not covered by tests
);
assert!(
offset < layout::DRAM_MEM_MAX_SIZE,
"offset outside allowed DRAM range"

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L71 was not covered by tests
);

let dram_size = min(size, layout::DRAM_MEM_MAX_SIZE - offset);

if dram_size != size {
logger::warn!(
"Requested offset/memory size {}/{} exceeds architectural maximum (1022GiB). Size has \
been truncated to {}",

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

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/arch/aarch64/mod.rs#L78-L79

Added lines #L78 - L79 were not covered by tests
offset,
size,
dram_size
);
}

vec![(
GuestAddress(layout::DRAM_MEM_START + offset as u64),
dram_size,
)]
}

/// Configures the system for booting Linux.
Expand Down Expand Up @@ -89,7 +115,7 @@
// Configure vCPUs with normalizing and setting the generated CPU configuration.
for vcpu in vcpus.iter_mut() {
vcpu.kvm_vcpu.configure(
&vmm.guest_memory,
vmm.vm.guest_memory(),
entry_point,
&vcpu_config,
&optional_capabilities,
Expand All @@ -105,7 +131,7 @@
.expect("Cannot create cstring from cmdline string");

let fdt = fdt::create_fdt(
&vmm.guest_memory,
vmm.vm.guest_memory(),
vcpu_mpidr,
cmdline,
vmm.mmio_device_manager.get_device_info(),
Expand All @@ -114,8 +140,10 @@
initrd,
)?;

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

Ok(())
}
Expand Down Expand Up @@ -182,22 +210,61 @@
})
}

#[cfg(kani)]
mod verification {
use vm_memory::GuestAddress;

use crate::arch::aarch64::layout;
use crate::arch::arch_memory_regions;

#[kani::proof]
#[kani::unwind(3)]
fn verify_arch_memory_regions() {
let offset: u64 = kani::any::<u64>();
let len: u64 = kani::any::<u64>();

kani::assume(len > 0);
kani::assume(offset.checked_add(len).is_some());
kani::assume(offset < layout::DRAM_MEM_MAX_SIZE as u64);

let regions = arch_memory_regions(offset as usize, len as usize);

// No MMIO gap on ARM
assert_eq!(regions.len(), 1);

let (GuestAddress(start), actual_len) = regions[0];
let actual_len = actual_len as u64;

assert_eq!(start, layout::DRAM_MEM_START + offset);
assert!(actual_len <= layout::DRAM_MEM_MAX_SIZE as u64);
assert!(actual_len <= len);

if actual_len < len {
assert_eq!(
start + actual_len,
layout::DRAM_MEM_START + layout::DRAM_MEM_MAX_SIZE as u64
);
assert!(offset + len >= layout::DRAM_MEM_MAX_SIZE as u64);
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::arch_mem;

#[test]
fn test_regions_lt_1024gb() {
let regions = arch_memory_regions(1usize << 29);
let regions = arch_memory_regions(0, 1usize << 29);
assert_eq!(1, regions.len());
assert_eq!(GuestAddress(super::layout::DRAM_MEM_START), regions[0].0);
assert_eq!(1usize << 29, regions[0].1);
}

#[test]
fn test_regions_gt_1024gb() {
let regions = arch_memory_regions(1usize << 41);
let regions = arch_memory_regions(0, 1usize << 41);
assert_eq!(1, regions.len());
assert_eq!(GuestAddress(super::layout::DRAM_MEM_START), regions[0].0);
assert_eq!(super::layout::DRAM_MEM_MAX_SIZE, regions[0].1);
Expand Down
43 changes: 21 additions & 22 deletions src/vmm/src/arch/aarch64/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,27 +502,26 @@ mod tests {
use crate::test_utils::arch_mem;
use crate::vcpu::VcpuConfig;
use crate::vstate::kvm::Kvm;
use crate::vstate::memory::GuestMemoryMmap;
use crate::vstate::vm::Vm;
use crate::vstate::vm::tests::setup_vm_with_memory;

fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, KvmVcpu, GuestMemoryMmap) {
let (kvm, mut vm, mut vcpu, vm_mem) = setup_vcpu_no_init(mem_size);
fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, KvmVcpu) {
let (kvm, mut vm, mut vcpu) = setup_vcpu_no_init(mem_size);
vcpu.init(&[]).unwrap();
vm.setup_irqchip(1).unwrap();
(kvm, vm, vcpu, vm_mem)
(kvm, vm, vcpu)
}

fn setup_vcpu_no_init(mem_size: usize) -> (Kvm, Vm, KvmVcpu, GuestMemoryMmap) {
let (kvm, vm, vm_mem) = setup_vm_with_memory(mem_size);
fn setup_vcpu_no_init(mem_size: usize) -> (Kvm, Vm, KvmVcpu) {
let (kvm, vm) = setup_vm_with_memory(mem_size);
let vcpu = KvmVcpu::new(0, &vm).unwrap();

(kvm, vm, vcpu, vm_mem)
(kvm, vm, vcpu)
}

#[test]
fn test_create_vcpu() {
let (_, vm, _) = setup_vm_with_memory(0x1000);
let (_, vm) = setup_vm_with_memory(0x1000);

unsafe { libc::close(vm.fd().as_raw_fd()) };

Expand All @@ -538,7 +537,7 @@ mod tests {

#[test]
fn test_configure_vcpu() {
let (kvm, _, mut vcpu, vm_mem) = setup_vcpu(0x10000);
let (kvm, vm, mut vcpu) = setup_vcpu(0x10000);
let optional_capabilities = kvm.optional_capabilities();

let vcpu_config = VcpuConfig {
Expand All @@ -548,7 +547,7 @@ mod tests {
};

vcpu.configure(
&vm_mem,
vm.guest_memory(),
EntryPoint {
entry_addr: GuestAddress(crate::arch::get_kernel_start()),
protocol: BootProtocol::LinuxBoot,
Expand All @@ -561,7 +560,7 @@ mod tests {
unsafe { libc::close(vcpu.fd.as_raw_fd()) };

let err = vcpu.configure(
&vm_mem,
vm.guest_memory(),
EntryPoint {
entry_addr: GuestAddress(crate::arch::get_kernel_start()),
protocol: BootProtocol::LinuxBoot,
Expand All @@ -583,7 +582,7 @@ mod tests {

#[test]
fn test_init_vcpu() {
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
let (_, mut vm) = setup_vm_with_memory(0x1000);
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
vm.setup_irqchip(1).unwrap();

Expand All @@ -602,7 +601,7 @@ mod tests {

#[test]
fn test_vcpu_save_restore_state() {
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
let (_, mut vm) = setup_vm_with_memory(0x1000);
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
vm.setup_irqchip(1).unwrap();

Expand Down Expand Up @@ -646,7 +645,7 @@ mod tests {
//
// This should fail with ENOEXEC.
// https://elixir.bootlin.com/linux/v5.10.176/source/arch/arm64/kvm/arm.c#L1165
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
let (_, mut vm) = setup_vm_with_memory(0x1000);
let vcpu = KvmVcpu::new(0, &vm).unwrap();
vm.setup_irqchip(1).unwrap();

Expand All @@ -656,7 +655,7 @@ mod tests {
#[test]
fn test_dump_cpu_config_after_init() {
// Test `dump_cpu_config()` after `KVM_VCPU_INIT`.
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
let (_, mut vm) = setup_vm_with_memory(0x1000);
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
vm.setup_irqchip(1).unwrap();
vcpu.init(&[]).unwrap();
Expand All @@ -666,7 +665,7 @@ mod tests {

#[test]
fn test_setup_non_boot_vcpu() {
let (_, vm, _) = setup_vm_with_memory(0x1000);
let (_, vm) = setup_vm_with_memory(0x1000);
let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap();
vcpu1.init(&[]).unwrap();
let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap();
Expand All @@ -678,22 +677,22 @@ mod tests {
// Test `get_regs()` with valid register IDs.
// - X0: 0x6030 0000 0010 0000
// - X1: 0x6030 0000 0010 0002
let (_, _, vcpu, _) = setup_vcpu(0x10000);
let (_, _, vcpu) = setup_vcpu(0x10000);
let reg_list = Vec::<u64>::from([0x6030000000100000, 0x6030000000100002]);
get_registers(&vcpu.fd, &reg_list, &mut Aarch64RegisterVec::default()).unwrap();
}

#[test]
fn test_get_invalid_regs() {
// Test `get_regs()` with invalid register IDs.
let (_, _, vcpu, _) = setup_vcpu(0x10000);
let (_, _, vcpu) = setup_vcpu(0x10000);
let reg_list = Vec::<u64>::from([0x6030000000100001, 0x6030000000100003]);
get_registers(&vcpu.fd, &reg_list, &mut Aarch64RegisterVec::default()).unwrap_err();
}

#[test]
fn test_setup_regs() {
let (kvm, _, vcpu, _) = setup_vcpu_no_init(0x10000);
let (kvm, _, vcpu) = setup_vcpu_no_init(0x10000);
let mem = arch_mem(layout::FDT_MAX_SIZE + 0x1000);
let optional_capabilities = kvm.optional_capabilities();

Expand Down Expand Up @@ -730,7 +729,7 @@ mod tests {

#[test]
fn test_read_mpidr() {
let (_, _, vcpu, _) = setup_vcpu_no_init(0x10000);
let (_, _, vcpu) = setup_vcpu_no_init(0x10000);

// Must fail when vcpu is not initialized yet.
let res = vcpu.get_mpidr();
Expand All @@ -745,7 +744,7 @@ mod tests {

#[test]
fn test_get_set_regs() {
let (_, _, vcpu, _) = setup_vcpu_no_init(0x10000);
let (_, _, vcpu) = setup_vcpu_no_init(0x10000);

// Must fail when vcpu is not initialized yet.
let mut regs = Aarch64RegisterVec::default();
Expand All @@ -763,7 +762,7 @@ mod tests {
fn test_mpstate() {
use std::os::unix::io::AsRawFd;

let (_, _, vcpu, _) = setup_vcpu(0x10000);
let (_, _, vcpu) = setup_vcpu(0x10000);

let res = vcpu.get_mpstate();
vcpu.set_mpstate(res.unwrap()).unwrap();
Expand Down
Loading