Compare commits

...

18 Commits
main ... v32.1

Author SHA1 Message Date
Bo Chen
4eb4dee9cf build: Release v32.1 (bug fix release)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-06 14:55:16 -07:00
Bo Chen
aa850b5c3d misc: Address clippy beta issues
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Alyssa Ross
c84d43d85d vmm: ignore and warn TAP FDs send in vm.create
This does the same thing as df2a7c17 ("vmm: Ignore and warn TAP FDs
sent via the HTTP request body"), but for the vm.create endpoint,
which also previously would accept file descriptors in the body, and
try to use whatever fd occupied that number as a TAP device.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Alyssa Ross
4eac4dfb0f vmm: allow restart_syscall() in PTY process
This can be triggered by debugging cloud-hypervisor using gdb, or
probably if the process is suspended and restarted.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/5489
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Jianyong Wu
475d1879f2 vfio: fix vfio device fail to initialize issue for 64k page size
Currently, vfio device fails to initialize as the msix-cap region in BAR
is mapped as RW region.

To resolve the initialization issue, this commit avoids mapping the
msix-cap region in the BAR. However, this solution introduces another
problem where aligning the msix table offset in the BAR to the page
size may cause overlap with the MMIO RW region, leading to reduced
performance. By enlarging the entire region in the BAR and relocating
the msix table to achieve page size alignment, this problem can be
overcomed effectively.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
42ad38d28c vm-allocator: Add page size related functions
To avoid code duplication extract page related functions to their
own module and add utility functions for manipulating addresses
related to page sizes

Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Bo Chen
8a8fe39c99 vmm: Clarify memory regions are required to be page-size aligned
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Bo Chen
7da34333f5 arch: Refactor the way of creating memory mapping
This patch clarifies the assumptions we have regarding the guest address
space layout while creating memory mapping in E820 on x86_64 and fdt on
aarch64. It also explicitly checks on these assumptions and report
errors if these assumptions do not hold.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Yu Li
1da2abf4b5 vmm: memory_manager: align down the rest space of ram_region
This commit renames `ram_region_sub_size` to `ram_region_available_size`
and make its value align down to the default page size or hugepage
size of the current memory zone, which can prevent the memory zone from
being split into misaligned parts.  And if the available size of ram
region is zero, this region will be marked as consumed even it has
unused space.

Note that there is two methods to use hugepages.

1. Specify `hugepages` for `memory` or `memory-zone`, if the
   `hugepage_size` is not specified, the value can be got by `statfs`
   for `/dev/hugepages`.
2. Specify a `file` in hugetlbfs for `memory-zone`, the hugepage size
   can also be got by `statfs` for the file.

The value for alignment will be the hugepage size if this memory zone
is using hugepages, otherwise the value will be default page size of
system.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
e5835fdc75 arch: let arch_memory_regions return all available regions
The previous `arch_memory_regions` function will provide some memory
regions with the specified memory size and fill all the previous
regions before using the next one, but sometimes there may be no need
to fill up the previous one, e.g., the previous one should be aligned
with hugepage size.

This commit make `arch_memory_regions` function not take any
parameters and return the max available regions, the memory manager
can use them on demand.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
c10de48f08 arch: create memory mapping by the actual memory info
The original codes did not consider that the previous memory region
might not be full and always set it to the maximum size.

This commit fixes this problem by creating memory mappings based on
the actual memory details in both E820 on x86_64 and fdt on aarch64.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
5b0478d12a logger: use write with \r\n instead of writeln
The device manager will set tty or pty to raw mode, all the `\n` will
be LF without CR, which makes the output difficult to read.

This commit solves it by using `write` with `\r\n` instead of
`writeln`, which can print CR and LF explicitly.

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
d1504e5654 vmm: use unwrap_or instead of match for prefault
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
d86905b92a arch: x86_64: Populate the APIC Id
Program the APIC ID (CPUID leaf 0x1 EBX) with the CPU id. This resolves
an issue where the EDKII firmware expects the APIC ID to vary per-CPU.

Fixes: #5475
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Alyssa Ross
f0de3e5b55 vmm, virtio-devices: allow mremap for consoles
SerialBuffer uses VecDeque::extend, which calls realloc, which a
maximum buffer size of 1 MiB.  Starting at allocation sizes of
128 KiB, musl's mallocng allocator will use mremap for the allocation.
Since this was not permitted by the seccomp rules, heavy write load
could crash cloud-hypervisor with a seccomp failure.  (Encountered
using virtio-console, but I don't see any reason it wouldn't happen
for the legacy serial device too.)

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Rafael Mendonca
81a4274738 main: Fix error propagation if starting the VM fails
Commit 21d40d7 ("main: reset tty if starting the VM fails") changed
start_vmm() to join the vmm thread if an error happens after the vmm
thread is started. The implementation put all the error-prone code that
is run after the vmm is started in a closure, to be able to always join
the vmm thread, regardless of any error happening. However, it missed
propagating the error that might happen inside the closure back to the
main function, after joining the vmm thread.

For some cmd line options, the above issue inhibits proper error
reporting when starting a VM with invalid commands, as many parameters
are parsed after the vmm is started, thus if such parsing fails, no
error will be reported back to the user.

See: #5435
Fixes: 21d40d7 ("main: reset tty if starting the VM fails")
Signed-off-by: Rafael Mendonca <rafaelmendsr@gmail.com>
2023-07-05 09:36:22 -07:00
Bo Chen
ea1a05f846 vmm: Allocate guest memory address space before TDX initialization
The refactoring on deferring address space allocation (#5169) broke TDX,
as TDX initialization needs to access guest memory for encryption and
measurement of guest pages.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
378eadbc1a vfio: align memory region size and address to PAGE_SIZE
In current implementation, memory region used in vfio is assumed to
align to 4k which may cause error when the PAGE_SIZE is not 4k, like on
Arm, it can be 16k and 64k.

Remove this assumption and align memory resource used by vfio to
PAGE_SIZE then vfio can run on host with 64k PAGE_SIZE.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
23 changed files with 528 additions and 272 deletions

2
Cargo.lock generated
View File

@@ -179,7 +179,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloud-hypervisor"
version = "32.0.0"
version = "32.1.0"
dependencies = [
"anyhow",
"api_client",

View File

@@ -1,6 +1,6 @@
[package]
name = "cloud-hypervisor"
version = "32.0.0"
version = "32.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
default-run = "cloud-hypervisor"

View File

@@ -260,32 +260,86 @@ fn create_memory_node(
fdt.end_node(memory_node)?;
}
} else {
let last_addr = guest_mem.last_addr().raw_value();
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
// Case 1: all RAM is under the hole
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
let memory_node = fdt.begin_node("memory")?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?;
fdt.end_node(memory_node)?;
} else {
// Case 2: RAM is split by the hole
// Region 1: RAM before the hole
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
- super::layout::RAM_START.raw_value();
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
let ram_regions = {
let mut ram_regions = Vec::new();
let mut current_start = guest_mem
.iter()
.next()
.map(GuestMemoryRegion::start_addr)
.expect("GuestMemory must have one memory region at least")
.raw_value();
let mut current_end = current_start;
for (start, size) in guest_mem
.iter()
.map(|m| (m.start_addr().raw_value(), m.len()))
{
if current_end == start {
// This zone is continuous with the previous one.
current_end += size;
} else {
ram_regions.push((current_start, current_end));
current_start = start;
current_end = start + size;
}
}
ram_regions.push((current_start, current_end));
ram_regions
};
if ram_regions.len() > 2 {
panic!(
"There should be up to two non-continuous regions, devidided by the
gap at the end of 32bit address space."
);
}
// Create the memory node for memory region before the gap
{
let (first_region_start, first_region_end) = ram_regions
.first()
.expect("There should be at last one memory region");
let ram_start = super::layout::RAM_START.raw_value();
let mem_32bit_reserved_start = super::layout::MEM_32BIT_RESERVED_START.raw_value();
if !((first_region_start <= &ram_start)
&& (first_region_end > &ram_start)
&& (first_region_end <= &mem_32bit_reserved_start))
{
panic!(
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
);
}
let mem_size = first_region_end - ram_start;
let mem_reg_prop = [ram_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_start);
let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?;
fdt.end_node(memory_node)?;
}
// Region 2: RAM after the hole
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size];
let memory_node_name =
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
// Create the memory map entry for memory region after the gap if any
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
let ram_64bit_start = super::layout::RAM_64BIT_START.raw_value();
if second_region_start != &ram_64bit_start {
panic!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
);
}
let mem_size = second_region_end - ram_64bit_start;
let mem_reg_prop = [ram_64bit_start, mem_size];
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
let memory_node = fdt.begin_node(&memory_node_name)?;
fdt.property_string("device_type", "memory")?;
fdt.property_array_u64("reg", &mem_reg_prop)?;

View File

@@ -19,7 +19,7 @@ use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize};
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
pub const _NSIG: i32 = 65;
@@ -83,8 +83,8 @@ pub fn configure_vcpu(
Ok(mpidr)
}
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
let mut regions = vec![
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
vec![
// 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices
(
GuestAddress(0),
@@ -103,39 +103,21 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
layout::PCI_MMCONFIG_SIZE as usize,
RegionType::Reserved,
),
];
let ram_32bit_space_size =
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START);
// RAM space
// Case1: guest memory fits before the gap
if size <= ram_32bit_space_size {
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
// Case2: guest memory extends beyond the gap
} else {
// Push memory before the gap
regions.push((
// 1GiB ~ 4032 MiB: RAM before the gap
(
layout::RAM_START,
ram_32bit_space_size as usize,
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize,
RegionType::Ram,
));
// Other memory is placed after 4GiB
regions.push((
layout::RAM_64BIT_START,
(size - ram_32bit_space_size) as usize,
RegionType::Ram,
));
}
// Add the 32-bit reserved memory hole as a reserved region
regions.push((
layout::MEM_32BIT_RESERVED_START,
layout::MEM_32BIT_RESERVED_SIZE as usize,
RegionType::Reserved,
));
regions
),
// 4GiB ~ inf: RAM after the gap
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
// Add the 32-bit reserved memory hole as a reserved region
(
layout::MEM_32BIT_RESERVED_START,
layout::MEM_32BIT_RESERVED_SIZE as usize,
RegionType::Reserved,
),
]
}
/// Configures the system and should be called once per vm before starting vcpu threads.
@@ -217,26 +199,12 @@ mod tests {
use super::*;
#[test]
fn test_arch_memory_regions_dram_2gb() {
let regions = arch_memory_regions((1usize << 31) as u64); //2GB
assert_eq!(5, regions.len());
assert_eq!(layout::RAM_START, regions[3].0);
assert_eq!((1usize << 31), regions[3].1);
assert_eq!(RegionType::Ram, regions[3].2);
assert_eq!(RegionType::Reserved, regions[4].2);
}
#[test]
fn test_arch_memory_regions_dram_4gb() {
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
let ram_32bit_space_size =
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
fn test_arch_memory_regions_dram() {
let regions = arch_memory_regions();
assert_eq!(6, regions.len());
assert_eq!(layout::RAM_START, regions[3].0);
assert_eq!(ram_32bit_space_size, regions[3].1);
assert_eq!(RegionType::Ram, regions[3].2);
assert_eq!(RegionType::Reserved, regions[5].2);
assert_eq!(RegionType::Ram, regions[4].2);
assert_eq!(((1usize << 32) - ram_32bit_space_size), regions[4].1);
}
}

View File

@@ -775,6 +775,13 @@ pub fn configure_vcpu(
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
// Set ApicId in cpuid for each vcpu
// SAFETY: get host cpuid when eax=1
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
cpu_ebx &= 0xffffff;
cpu_ebx |= (id as u32) << 24;
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
if !kvm_hyperv {
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
@@ -827,47 +834,29 @@ pub fn configure_vcpu(
/// These should be used to configure the GuestMemory structure for the platform.
/// For x86_64 all addresses are valid from the start of the kernel except a
/// carve out at the end of 32bit address space.
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
let reserved_memory_gap_start = layout::MEM_32BIT_RESERVED_START
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
.expect("32-bit reserved region is too large");
let requested_memory_size = GuestAddress(size);
let mut regions = Vec::new();
// case1: guest memory fits before the gap
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
// case2: guest memory extends beyond the gap
} else {
// push memory before the gap
regions.push((
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
vec![
// 0 GiB ~ 3GiB: memory before the gap
(
GuestAddress(0),
layout::MEM_32BIT_RESERVED_START.raw_value() as usize,
RegionType::Ram,
));
regions.push((
layout::RAM_64BIT_START,
requested_memory_size.unchecked_offset_from(layout::MEM_32BIT_RESERVED_START) as usize,
RegionType::Ram,
));
}
// Add the 32-bit device memory hole as a sub region.
regions.push((
layout::MEM_32BIT_RESERVED_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
));
// Add the 32-bit reserved memory hole as a sub region.
regions.push((
reserved_memory_gap_start,
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
RegionType::Reserved,
));
regions
),
// 4 GiB ~ inf: memory after the gap
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
// 3 GiB ~ 3712 MiB: 32-bit device memory hole
(
layout::MEM_32BIT_RESERVED_START,
layout::MEM_32BIT_DEVICES_SIZE as usize,
RegionType::SubRegion,
),
// 3712 MiB ~ 3968 MiB: 32-bit reserved memory hole
(
layout::MEM_32BIT_RESERVED_START.unchecked_add(layout::MEM_32BIT_DEVICES_SIZE),
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
RegionType::Reserved,
),
]
}
/// Configures the system and should be called once per vm before starting vcpu threads.
@@ -965,30 +954,102 @@ fn configure_pvh(
// Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
let mem_end = guest_mem.last_addr();
// Merge continuous memory regions into one region.
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
let ram_regions = {
let mut ram_regions = Vec::new();
let mut current_start = guest_mem
.iter()
.next()
.map(GuestMemoryRegion::start_addr)
.expect("GuestMemory must have one memory region at least")
.raw_value();
let mut current_end = current_start;
if mem_end < layout::MEM_32BIT_RESERVED_START {
add_memmap_entry(
&mut memmap,
layout::HIGH_RAM_START.raw_value(),
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
E820_RAM,
);
} else {
add_memmap_entry(
&mut memmap,
layout::HIGH_RAM_START.raw_value(),
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
E820_RAM,
);
if mem_end > layout::RAM_64BIT_START {
add_memmap_entry(
&mut memmap,
layout::RAM_64BIT_START.raw_value(),
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
E820_RAM,
);
for (start, size) in guest_mem
.iter()
.map(|m| (m.start_addr().raw_value(), m.len()))
{
if current_end == start {
// This zone is continuous with the previous one.
current_end += size;
} else {
ram_regions.push((current_start, current_end));
current_start = start;
current_end = start + size;
}
}
ram_regions.push((current_start, current_end));
ram_regions
};
if ram_regions.len() > 2 {
error!(
"There should be up to two non-continuous regions, devidided by the
gap at the end of 32bit address space (e.g. between 3G and 4G)."
);
return Err(super::Error::MemmapTableSetup);
}
// Create the memory map entry for memory region before the gap
{
let (first_region_start, first_region_end) =
ram_regions.first().ok_or(super::Error::MemmapTableSetup)?;
let high_ram_start = layout::HIGH_RAM_START.raw_value();
let mem_32bit_reserved_start = layout::MEM_32BIT_RESERVED_START.raw_value();
if !((first_region_start <= &high_ram_start)
&& (first_region_end > &high_ram_start)
&& (first_region_end <= &mem_32bit_reserved_start))
{
error!(
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
high_ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
first_region_start, first_region_end, high_ram_start, mem_32bit_reserved_start
);
return Err(super::Error::MemmapTableSetup);
}
info!(
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
high_ram_start, first_region_end
);
add_memmap_entry(
&mut memmap,
high_ram_start,
first_region_end - high_ram_start,
E820_RAM,
);
}
// Create the memory map entry for memory region after the gap if any
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
let ram_64bit_start = layout::RAM_64BIT_START.raw_value();
if second_region_start != &ram_64bit_start {
error!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
);
return Err(super::Error::MemmapTableSetup);
}
info!(
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
ram_64bit_start, second_region_end
);
add_memmap_entry(
&mut memmap,
ram_64bit_start,
second_region_end - ram_64bit_start,
E820_RAM,
);
}
add_memmap_entry(
@@ -1224,16 +1285,8 @@ mod tests {
use super::*;
#[test]
fn regions_lt_4gb() {
let regions = arch_memory_regions(1 << 29);
assert_eq!(3, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(1usize << 29, regions[0].1);
}
#[test]
fn regions_gt_4gb() {
let regions = arch_memory_regions((1 << 32) + 0x8000);
fn regions_base_addr() {
let regions = arch_memory_regions();
assert_eq!(4, regions.len());
assert_eq!(GuestAddress(0), regions[0].0);
assert_eq!(GuestAddress(1 << 32), regions[1].0);
@@ -1257,11 +1310,10 @@ mod tests {
assert!(config_err.is_err());
// Now assigning some memory that falls before the 32bit memory hole.
let mem_size = 128 << 20;
let arch_mem_regions = arch_memory_regions(mem_size);
let arch_mem_regions = arch_memory_regions();
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter()
.filter(|r| r.2 == RegionType::Ram)
.filter(|r| r.2 == RegionType::Ram && r.1 != usize::MAX)
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
@@ -1279,48 +1331,18 @@ mod tests {
)
.unwrap();
// Now assigning some memory that is equal to the start of the 32bit memory hole.
let mem_size = 3328 << 20;
let arch_mem_regions = arch_memory_regions(mem_size);
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter()
.filter(|r| r.2 == RegionType::Ram)
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(
&gm,
GuestAddress(0),
&None,
no_vcpus,
None,
None,
None,
None,
None,
)
.unwrap();
configure_system(
&gm,
GuestAddress(0),
&None,
no_vcpus,
None,
None,
None,
None,
None,
)
.unwrap();
// Now assigning some memory that falls after the 32bit memory hole.
let mem_size = 3330 << 20;
let arch_mem_regions = arch_memory_regions(mem_size);
let arch_mem_regions = arch_memory_regions();
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter()
.filter(|r| r.2 == RegionType::Ram)
.map(|r| (r.0, r.1))
.map(|r| {
if r.1 == usize::MAX {
(r.0, 128 << 20)
} else {
(r.0, r.1)
}
})
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(

View File

@@ -511,6 +511,16 @@ impl MsixCap {
self.pba & 0xffff_fff8
}
pub fn table_set_offset(&mut self, addr: u32) {
self.table &= 0x7;
self.table += addr;
}
pub fn pba_set_offset(&mut self, addr: u32) {
self.pba &= 0x7;
self.pba += addr;
}
pub fn table_bir(&self) -> u32 {
self.table & 0x7
}

View File

@@ -14,6 +14,7 @@ use crate::{
use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian};
use hypervisor::HypervisorVmError;
use libc::{sysconf, _SC_PAGESIZE};
use std::any::Any;
use std::collections::{BTreeMap, HashMap};
use std::io;
@@ -27,6 +28,9 @@ use vfio_bindings::bindings::vfio::*;
use vfio_ioctls::{
VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea,
};
use vm_allocator::page_size::{
align_page_size_down, align_page_size_up, is_4k_aligned, is_4k_multiple, is_page_size_aligned,
};
use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
@@ -498,6 +502,29 @@ impl VfioCommon {
Ok(vfio_common)
}
/// In case msix table offset is not page size aligned, we need do some fixup to achive it.
/// Becuse we don't want the MMIO RW region and trap region overlap each other.
fn fixup_msix_region(&mut self, bar_id: u32, region_size: u64) -> u64 {
let msix = self.interrupt.msix.as_mut().unwrap();
let msix_cap = &mut msix.cap;
// Suppose table_bir equals to pba_bir here. Am I right?
let (table_offset, table_size) = msix_cap.table_range();
if is_page_size_aligned(table_offset) || msix_cap.table_bir() != bar_id {
return region_size;
}
let (pba_offset, pba_size) = msix_cap.pba_range();
let msix_sz = align_page_size_up(table_size + pba_size);
// Expand region to hold RW and trap region which both page size aligned
let size = std::cmp::max(region_size * 2, msix_sz * 2);
// let table starts from the middle of the region
msix_cap.table_set_offset((size / 2) as u32);
msix_cap.pba_set_offset((size / 2 + pba_offset - table_offset) as u32);
size
}
pub(crate) fn allocate_bars(
&mut self,
allocator: &Arc<Mutex<SystemAllocator>>,
@@ -661,9 +688,16 @@ impl VfioCommon {
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
}
PciBarRegionType::Memory64BitRegion => {
// BAR allocation must be naturally aligned
// We need do some fixup to keep MMIO RW region and msix cap region page size
// aligned.
region_size = self.fixup_msix_region(bar_id, region_size);
mmio_allocator
.allocate(restored_bar_addr, region_size, Some(region_size))
.allocate(
restored_bar_addr,
region_size,
// SAFETY: FFI call. Trivially safe.
Some(unsafe { sysconf(_SC_PAGESIZE) as GuestUsize }),
)
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
}
};
@@ -800,6 +834,23 @@ impl VfioCommon {
});
}
pub(crate) fn get_msix_cap_idx(&self) -> Option<usize> {
let mut cap_next = self
.vfio_wrapper
.read_config_byte(PCI_CONFIG_CAPABILITY_OFFSET);
while cap_next != 0 {
let cap_id = self.vfio_wrapper.read_config_byte(cap_next.into());
if PciCapabilityId::from(cap_id) == PciCapabilityId::MsiX {
return Some(cap_next as usize);
} else {
cap_next = self.vfio_wrapper.read_config_byte((cap_next + 1).into());
}
}
None
}
pub(crate) fn parse_capabilities(&mut self, bdf: PciBdf) {
let mut cap_next = self
.vfio_wrapper
@@ -1154,6 +1205,15 @@ impl VfioCommon {
return self.configuration.read_reg(reg_idx);
}
if let Some(id) = self.get_msix_cap_idx() {
let msix = self.interrupt.msix.as_mut().unwrap();
if reg_idx * 4 == id + 4 {
return msix.cap.table;
} else if reg_idx * 4 == id + 8 {
return msix.cap.pba;
}
}
// Since we don't support passing multi-functions devices, we should
// mask the multi-function bit, bit 7 of the Header Type byte on the
// register 3.
@@ -1316,18 +1376,6 @@ impl VfioPciDevice {
self.iommu_attached
}
fn align_4k(address: u64) -> u64 {
(address + 0xfff) & 0xffff_ffff_ffff_f000
}
fn is_4k_aligned(address: u64) -> bool {
(address & 0xfff) == 0
}
fn is_4k_multiple(size: u64) -> bool {
(size & 0xfff) == 0
}
fn generate_sparse_areas(
caps: &[VfioRegionInfoCap],
region_index: u32,
@@ -1339,14 +1387,14 @@ impl VfioPciDevice {
match cap {
VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()),
VfioRegionInfoCap::MsixMappable => {
if !Self::is_4k_aligned(region_start) {
if !is_4k_aligned(region_start) {
error!(
"Region start address 0x{:x} must be at least aligned on 4KiB",
region_start
);
return Err(VfioPciError::RegionAlignment);
}
if !Self::is_4k_multiple(region_size) {
if !is_4k_multiple(region_size) {
error!(
"Region size 0x{:x} must be at least a multiple of 4KiB",
region_size
@@ -1358,7 +1406,8 @@ impl VfioPciDevice {
// the MSI-X PBA table, we must calculate the subregions
// around them, leading to a list of sparse areas.
// We want to make sure we will still trap MMIO accesses
// to these MSI-X specific ranges.
// to these MSI-X specific ranges. If these region don't align
// with pagesize, we can achive it by enlarging its range.
//
// Using a BtreeMap as the list provided through the iterator is sorted
// by key. This ensures proper split of the whole region.
@@ -1366,10 +1415,14 @@ impl VfioPciDevice {
if let Some(msix) = vfio_msix {
if region_index == msix.cap.table_bir() {
let (offset, size) = msix.cap.table_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size);
}
if region_index == msix.cap.pba_bir() {
let (offset, size) = msix.cap.pba_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size);
}
}
@@ -1383,8 +1436,7 @@ impl VfioPciDevice {
size: range_offset - current_offset,
});
}
current_offset = Self::align_4k(range_offset + range_size);
current_offset = align_page_size_down(range_offset + range_size);
}
if region_size > current_offset {
@@ -1482,6 +1534,15 @@ impl VfioPciDevice {
return Err(VfioPciError::MmapArea);
}
if !is_page_size_aligned(area.size) || !is_page_size_aligned(area.offset) {
warn!(
"Could not mmap sparse area that is not page size aligned (offset = 0x{:x}, size = 0x{:x})",
area.offset,
area.size,
);
return Ok(());
}
let user_memory_region = UserMemoryRegion {
slot: (self.memory_slot)(),
start: region.start.0 + area.offset,

View File

@@ -1,3 +1,4 @@
- [v32.1](#v321)
- [v32.0](#v320)
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
- [API Changes](#api-changes)
@@ -282,6 +283,19 @@
- [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization)
# v32.1
This is a bug fix release. The following issues have been addressed:
* Report errors explicitly to users when VM failed to boot (#5453)
* Fix VFIO on platforms with non-4k page size (#5450, #5469)
* Fix TDX initialization (#5454)
* Ensure all guest memory regions are page-size aligned (#5496)
* Fix seccomp filter lists related to virtio-console, serial and pty
(#5506, #5524)
* Populate APIC ID properly (#5512)
* Ignore and warn TAP FDs in more situations (#5522)
# v32.0
This release has been tracked in our [roadmap

View File

@@ -92,9 +92,9 @@ impl log::Log for Logger {
let duration = now.duration_since(self.start);
if record.file().is_some() && record.line().is_some() {
writeln!(
write!(
*(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}",
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}\r\n",
duration,
std::thread::current().name().unwrap_or("anonymous"),
record.level(),
@@ -103,9 +103,9 @@ impl log::Log for Logger {
record.args()
)
} else {
writeln!(
write!(
*(*(self.output.lock().unwrap())),
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}",
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}\r\n",
duration,
std::thread::current().name().unwrap_or("anonymous"),
record.level(),
@@ -573,7 +573,7 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
.map_err(Error::ThreadJoin)?
.map_err(Error::VmmThread)?;
Ok(api_socket_path)
r.map(|_| api_socket_path)
}
fn main() {

View File

@@ -309,11 +309,7 @@ impl Emulator {
}
self.established_flag_cached = true;
if est.resp.bit != 0 {
self.established_flag = false;
} else {
self.established_flag = true;
}
self.established_flag = est.resp.bit == 0;
self.established_flag
}

View File

@@ -145,11 +145,7 @@ impl DiskSpec {
let bits = f
.read_u32::<LittleEndian>()
.map_err(VhdxMetadataError::ReadMetadata)?;
if bits & BLOCK_HAS_PARENT != 0 {
disk_spec.has_parent = true;
} else {
disk_spec.has_parent = false;
}
disk_spec.has_parent = bits & BLOCK_HAS_PARENT != 0;
metadata_presence |= METADATA_FILE_PARAMETER_PRESENT;
} else if metadata_entry.item_id

View File

@@ -259,6 +259,7 @@ fn virtio_thread_common() -> Vec<(i64, Vec<SeccompRule>)> {
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_mprotect, vec![]),
(libc::SYS_mremap, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_openat, vec![]),
(libc::SYS_read, vec![]),

View File

@@ -605,7 +605,7 @@ impl VirtioDevice for Fs {
&mut self,
shm_regions: VirtioSharedMemoryList,
) -> std::result::Result<(), crate::Error> {
if let Some(mut cache) = self.cache.as_mut() {
if let Some(cache) = self.cache.as_mut() {
cache.0 = shm_regions;
Ok(())
} else {

View File

@@ -12,6 +12,8 @@
mod address;
mod gsi;
/// page size related utility funtions
pub mod page_size;
mod system;
pub use crate::address::AddressAllocator;

View File

@@ -0,0 +1,38 @@
// Copyright 2023 Arm Limited (or its affiliates). All rights reserved.
// SPDX-License-Identifier: Apache-2.0
use libc::{sysconf, _SC_PAGESIZE};
/// get host page size
pub fn get_page_size() -> u64 {
// SAFETY: FFI call. Trivially safe.
unsafe { sysconf(_SC_PAGESIZE) as u64 }
}
/// round up address to let it align page size
pub fn align_page_size_up(address: u64) -> u64 {
let page_size = get_page_size();
(address + page_size - 1) & !(page_size - 1)
}
/// round down address to let it align page size
pub fn align_page_size_down(address: u64) -> u64 {
let page_size = get_page_size();
address & !(page_size - 1)
}
/// Test if address is 4k aligned
pub fn is_4k_aligned(address: u64) -> bool {
(address & 0xfff) == 0
}
/// Test if size is 4k aligned
pub fn is_4k_multiple(size: u64) -> bool {
(size & 0xfff) == 0
}
/// Test if address is page size aligned
pub fn is_page_size_aligned(address: u64) -> bool {
let page_size = get_page_size();
address & (page_size - 1) == 0
}

View File

@@ -14,14 +14,7 @@ use crate::gsi::GsiAllocator;
#[cfg(target_arch = "x86_64")]
use crate::gsi::GsiApic;
use libc::{sysconf, _SC_PAGESIZE};
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
fn pagesize() -> usize {
// SAFETY: FFI call. Trivially safe.
unsafe { sysconf(_SC_PAGESIZE) as usize }
}
use crate::page_size::get_page_size;
/// Manages allocating system resources such as address space and interrupt numbers.
///
@@ -126,7 +119,7 @@ impl SystemAllocator {
self.platform_mmio_address_space.allocate(
address,
size,
Some(align_size.unwrap_or(pagesize() as u64)),
Some(align_size.unwrap_or(get_page_size())),
)
}
@@ -140,7 +133,7 @@ impl SystemAllocator {
self.mmio_hole_address_space.allocate(
address,
size,
Some(align_size.unwrap_or(pagesize() as u64)),
Some(align_size.unwrap_or(get_page_size())),
)
}

View File

@@ -110,8 +110,7 @@ impl Bus {
let devices = self.devices.read().unwrap();
let (range, dev) = devices
.range(..=BusRange { base: addr, len: 1 })
.rev()
.next()?;
.next_back()?;
dev.upgrade().map(|d| (*range, d.clone()))
}

View File

@@ -13,7 +13,7 @@ pub mod protocol;
/// Global VMM version for versioning
const MAJOR_VERSION: u16 = 32;
const MINOR_VERSION: u16 = 0;
const MINOR_VERSION: u16 = 1;
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
pub trait VersionMapped {

View File

@@ -36,13 +36,22 @@ impl EndpointHandler for VmCreate {
match &req.body {
Some(body) => {
// Deserialize into a VmConfig
let vm_config: VmConfig = match serde_json::from_slice(body.raw())
let mut vm_config: VmConfig = match serde_json::from_slice(body.raw())
.map_err(HttpError::SerdeJsonDeserialize)
{
Ok(config) => config,
Err(e) => return error_response(e, StatusCode::BadRequest),
};
if let Some(ref mut nets) = vm_config.net {
if nets.iter().any(|net| net.fds.is_some()) {
warn!("Ignoring FDs sent via the HTTP request body");
}
for net in nets {
net.fds = None;
}
}
// Call vm_create()
match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
.map_err(HttpError::ApiError)

View File

@@ -1131,7 +1131,7 @@ impl CpuManager {
fn remove_vcpu(&mut self, cpu_id: u8) -> Result<()> {
info!("Removing vCPU: cpu_id = {}", cpu_id);
let mut state = &mut self.vcpu_states[usize::from(cpu_id)];
let state = &mut self.vcpu_states[usize::from(cpu_id)];
state.kill.store(true, Ordering::SeqCst);
state.signal_thread();
state.join_thread()?;

View File

@@ -31,7 +31,7 @@ use std::convert::TryInto;
use std::ffi;
use std::fs::{File, OpenOptions};
use std::io::{self, Read};
use std::ops::Deref;
use std::ops::{BitAnd, Deref, Not, Sub};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::path::PathBuf;
use std::result;
@@ -330,6 +330,12 @@ pub enum Error {
/// Using a directory as a backing file for memory is not supported
DirectoryAsBackingFileForMemory,
/// Failed to stat filesystem
GetFileSystemBlockSize(io::Error),
/// Memory size is misaligned with default page size or its hugepage size
MisalignedMemorySize,
}
const ENABLE_FLAG: usize = 0;
@@ -353,6 +359,77 @@ fn mmio_address_space_size(phys_bits: u8) -> u64 {
(1 << phys_bits) - (1 << 16)
}
// The `statfs` function can get information of hugetlbfs, and the hugepage size is in the
// `f_bsize` field.
//
// See: https://github.com/torvalds/linux/blob/v6.3/fs/hugetlbfs/inode.c#L1169
fn statfs_get_bsize(path: &str) -> Result<u64, Error> {
let path = std::ffi::CString::new(path).map_err(|_| Error::InvalidMemoryParameters)?;
let mut buf = std::mem::MaybeUninit::<libc::statfs>::uninit();
// SAFETY: FFI call with a valid path and buffer
let ret = unsafe { libc::statfs(path.as_ptr(), buf.as_mut_ptr()) };
if ret != 0 {
return Err(Error::GetFileSystemBlockSize(
std::io::Error::last_os_error(),
));
}
// SAFETY: `buf` is valid at this point
// Because this value is always positive, just convert it directly.
// Note that the `f_bsize` is `i64` in glibc and `u64` in musl, using `as u64` will be warned
// by `clippy` on musl target. To avoid the warning, there should be `as _` instead of
// `as u64`.
let bsize = unsafe { (*buf.as_ptr()).f_bsize } as _;
Ok(bsize)
}
fn memory_zone_get_align_size(zone: &MemoryZoneConfig) -> Result<u64, Error> {
// SAFETY: FFI call. Trivially safe.
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 };
// There is no backend file and the `hugepages` is disabled, just use system page size.
if zone.file.is_none() && !zone.hugepages {
return Ok(page_size);
}
// The `hugepages` is enabled and the `hugepage_size` is specified, just use it directly.
if zone.hugepages && zone.hugepage_size.is_some() {
return Ok(zone.hugepage_size.unwrap());
}
// There are two scenarios here:
// - `hugepages` is enabled but `hugepage_size` is not specified:
// Call `statfs` for `/dev/hugepages` for getting the default size of hugepage
// - The backing file is specified:
// Call `statfs` for the file and get its `f_bsize`. If the value is larger than the page
// size of normal page, just use the `f_bsize` because the file is in a hugetlbfs. If the
// value is less than or equal to the page size, just use the page size.
let path = zone.file.as_ref().map_or(Ok("/dev/hugepages"), |pathbuf| {
pathbuf.to_str().ok_or(Error::InvalidMemoryParameters)
})?;
let align_size = std::cmp::max(page_size, statfs_get_bsize(path)?);
Ok(align_size)
}
#[inline]
fn align_down<T>(val: T, align: T) -> T
where
T: BitAnd<Output = T> + Not<Output = T> + Sub<Output = T> + From<u8>,
{
val & !(align - 1u8.into())
}
#[inline]
fn is_aligned<T>(val: T, align: T) -> bool
where
T: BitAnd<Output = T> + Sub<Output = T> + From<u8> + PartialEq,
{
(val & (align - 1u8.into())) == 0u8.into()
}
impl BusDevice for MemoryManager {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
if self.selected_slot < self.hotplug_slots.len() {
@@ -442,6 +519,9 @@ impl MemoryManager {
/// - First one mapping entirely the first memory zone on 0-1G range
/// - Second one mapping partially the second memory zone on 1G-3G range
/// - Third one mapping partially the second memory zone on 4G-6G range
/// Also, all memory regions are page-size aligned (e.g. their sizes must
/// be multiple of page-size), which may leave an additional hole in the
/// address space when hugepage is used.
fn create_memory_regions_from_zones(
ram_regions: &[(GuestAddress, usize)],
zones: &[MemoryZoneConfig],
@@ -451,9 +531,14 @@ impl MemoryManager {
let mut zones = zones.to_owned();
let mut mem_regions = Vec::new();
let mut zone = zones.remove(0);
let mut zone_offset = 0;
let mut zone_align_size = memory_zone_get_align_size(&zone)?;
let mut zone_offset = 0u64;
let mut memory_zones = HashMap::new();
if !is_aligned(zone.size, zone_align_size) {
return Err(Error::MisalignedMemorySize);
}
// Add zone id to the list of memory zones.
memory_zones.insert(zone.id.clone(), MemoryZone::default());
@@ -465,16 +550,20 @@ impl MemoryManager {
let mut ram_region_consumed = false;
let mut pull_next_zone = false;
let ram_region_sub_size = ram_region.1 - ram_region_offset;
let zone_sub_size = zone.size as usize - zone_offset;
let ram_region_available_size =
align_down(ram_region.1 as u64 - ram_region_offset, zone_align_size);
if ram_region_available_size == 0 {
break;
}
let zone_sub_size = zone.size - zone_offset;
let file_offset = zone_offset as u64;
let file_offset = zone_offset;
let region_start = ram_region
.0
.checked_add(ram_region_offset as u64)
.checked_add(ram_region_offset)
.ok_or(Error::GuestAddressOverFlow)?;
let region_size = if zone_sub_size <= ram_region_sub_size {
if zone_sub_size == ram_region_sub_size {
let region_size = if zone_sub_size <= ram_region_available_size {
if zone_sub_size == ram_region_available_size {
ram_region_consumed = true;
}
@@ -483,21 +572,24 @@ impl MemoryManager {
zone_sub_size
} else {
zone_offset += ram_region_sub_size;
zone_offset += ram_region_available_size;
ram_region_consumed = true;
ram_region_sub_size
ram_region_available_size
};
info!(
"create ram region for zone {}, region_start: {:#x}, region_size: {:#x}",
zone.id,
region_start.raw_value(),
region_size
);
let region = MemoryManager::create_ram_region(
&zone.file,
file_offset,
region_start,
region_size,
match prefault {
Some(pf) => pf,
None => zone.prefault,
},
region_size as usize,
prefault.unwrap_or(zone.prefault),
zone.shared,
zone.hugepages,
zone.hugepage_size,
@@ -522,6 +614,10 @@ impl MemoryManager {
break;
}
zone = zones.remove(0);
zone_align_size = memory_zone_get_align_size(&zone)?;
if !is_aligned(zone.size, zone_align_size) {
return Err(Error::MisalignedMemorySize);
}
// Check if zone id already exist. In case it does, throw
// an error as we need unique identifiers. Otherwise, add
@@ -573,10 +669,7 @@ impl MemoryManager {
guest_ram_mapping.file_offset,
GuestAddress(guest_ram_mapping.gpa),
guest_ram_mapping.size as usize,
match prefault {
Some(pf) => pf,
None => zone_config.prefault,
},
prefault.unwrap_or(zone_config.prefault),
zone_config.shared,
zone_config.hugepages,
zone_config.hugepage_size,
@@ -939,7 +1032,7 @@ impl MemoryManager {
)
} else {
// Init guest memory
let arch_mem_regions = arch::arch_memory_regions(ram_size);
let arch_mem_regions = arch::arch_memory_regions();
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
.iter()
@@ -997,10 +1090,7 @@ impl MemoryManager {
0,
start_addr,
hotplug_size as usize,
match prefault {
Some(pf) => pf,
None => zone.prefault,
},
prefault.unwrap_or(zone.prefault),
zone.shared,
zone.hugepages,
zone.hugepage_size,
@@ -1501,7 +1591,7 @@ impl MemoryManager {
.ok_or(Error::MemoryRangeAllocation)?;
// Update the slot so that it can be queried via the I/O port
let mut slot = &mut self.hotplug_slots[self.next_hotplug_slot];
let slot = &mut self.hotplug_slots[self.next_hotplug_slot];
slot.active = true;
slot.inserting = true;
slot.base = region.start_addr().0;

View File

@@ -484,6 +484,7 @@ fn pty_foreground_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, Backend
#[cfg(target_arch = "aarch64")]
(libc::SYS_ppoll, vec![]),
(libc::SYS_read, vec![]),
(libc::SYS_restart_syscall, vec![]),
(libc::SYS_rt_sigaction, vec![]),
(libc::SYS_rt_sigreturn, vec![]),
(libc::SYS_setsid, vec![]),
@@ -609,6 +610,7 @@ fn vmm_thread_rules(
(libc::SYS_socketpair, vec![]),
#[cfg(target_arch = "x86_64")]
(libc::SYS_stat, vec![]),
(libc::SYS_statfs, vec![]),
(libc::SYS_statx, vec![]),
(libc::SYS_tgkill, vec![]),
(libc::SYS_timerfd_create, vec![]),
@@ -715,6 +717,7 @@ fn vcpu_thread_rules(
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_mprotect, vec![]),
(libc::SYS_mremap, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_nanosleep, vec![]),
(libc::SYS_newfstatat, vec![]),

View File

@@ -1258,7 +1258,7 @@ impl Vm {
.resize(desired_memory)
.map_err(Error::MemoryManager)?;
let mut memory_config = &mut self.config.lock().unwrap().memory;
let memory_config = &mut self.config.lock().unwrap().memory;
if let Some(new_region) = &new_region {
self.device_manager
@@ -2004,6 +2004,18 @@ impl Vm {
})
.transpose()?;
#[cfg(target_arch = "x86_64")]
// Note: For x86, always call this function before invoking start boot vcpus.
// Otherwise guest would fail to boot because we haven't created the
// userspace mappings to update the hypervisor about the memory mappings.
// These mappings must be created before we start the vCPU threads for
// the very first time.
self.memory_manager
.lock()
.unwrap()
.allocate_address_space()
.map_err(Error::MemoryManager)?;
#[cfg(feature = "tdx")]
if let Some(hob_address) = hob_address {
// With the HOB address extracted the vCPUs can have
@@ -2021,18 +2033,6 @@ impl Vm {
self.vm.tdx_finalize().map_err(Error::FinalizeTdx)?;
}
#[cfg(target_arch = "x86_64")]
// Note: For x86, always call this function before invoking start boot vcpus.
// Otherwise guest would fail to boot because we haven't created the
// userspace mappings to update the hypervisor about the memory mappings.
// These mappings must be created before we start the vCPU threads for
// the very first time.
self.memory_manager
.lock()
.unwrap()
.allocate_address_space()
.map_err(Error::MemoryManager)?;
self.cpu_manager
.lock()
.unwrap()