mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
18 Commits
v51.1
...
stable/v32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eb4dee9cf | ||
|
|
aa850b5c3d | ||
|
|
c84d43d85d | ||
|
|
4eac4dfb0f | ||
|
|
475d1879f2 | ||
|
|
42ad38d28c | ||
|
|
8a8fe39c99 | ||
|
|
7da34333f5 | ||
|
|
1da2abf4b5 | ||
|
|
e5835fdc75 | ||
|
|
c10de48f08 | ||
|
|
5b0478d12a | ||
|
|
d1504e5654 | ||
|
|
d86905b92a | ||
|
|
f0de3e5b55 | ||
|
|
81a4274738 | ||
|
|
ea1a05f846 | ||
|
|
378eadbc1a |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -179,7 +179,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "32.0.0"
|
||||
version = "32.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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![]),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
mod address;
|
||||
mod gsi;
|
||||
/// page size related utility funtions
|
||||
pub mod page_size;
|
||||
mod system;
|
||||
|
||||
pub use crate::address::AddressAllocator;
|
||||
|
||||
38
vm-allocator/src/page_size.rs
Normal file
38
vm-allocator/src/page_size.rs
Normal 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
|
||||
}
|
||||
@@ -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())),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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![]),
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user