Skip to content

Commit 475a533

Browse files
authored
Merge branch 'firecracker-microvm:main' into main
2 parents e15ea97 + 01f90a8 commit 475a533

12 files changed

Lines changed: 110 additions & 9 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ tags
1818
/resources/x86_64
1919
/resources/aarch64
2020
redundant_seccomp_rules_build
21+
test_instrumented_firecracker_build

MAINTAINERS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ Firecracker is maintained by a dedicated team within Amazon:
99
- Jay Chung <jaehoc@amazon.com>
1010
- Marco Marangoni <mamarang@amazon.com>
1111
- Michael Zoumboulakis <zoumboul@amazon.com>
12+
- Pierre Bertholom <pbertho@amazon.com>
1213
- Riccardo Mancini <mancio@amazon.com>
1314
- Takahiro Itazuri <itazur@amazon.com>

src/vmm/src/devices/acpi/vmclock.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::devices::acpi::generated::vmclock_abi::{
1919
use crate::devices::legacy::EventFdTrigger;
2020
use crate::logger::debug;
2121
use crate::snapshot::Persist;
22-
use crate::vstate::memory::GuestMemoryMmap;
22+
use crate::vstate::memory::{GuestMemoryExtension, GuestMemoryMmap};
2323
use crate::vstate::resources::ResourceAllocator;
2424

2525
// SAFETY: `vmclock_abi` is a POD
@@ -114,6 +114,7 @@ impl VmClock {
114114

115115
/// Activate [`VmClock`] device
116116
pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), VmClockError> {
117+
mem.check_range_plugged(self.guest_address, self.inner.as_slice().len())?;
117118
mem.write_slice(self.inner.as_slice(), self.guest_address)?;
118119
Ok(())
119120
}

src/vmm/src/devices/acpi/vmgenid.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use vmm_sys_util::eventfd::EventFd;
1414
use super::super::legacy::EventFdTrigger;
1515
use crate::logger::debug;
1616
use crate::snapshot::Persist;
17-
use crate::vstate::memory::{Bytes, GuestMemoryMmap};
17+
use crate::vstate::memory::{Bytes, GuestMemoryExtension, GuestMemoryMmap};
1818
use crate::vstate::resources::ResourceAllocator;
1919

2020
/// Bytes of memory we allocate for VMGenID device
@@ -117,6 +117,7 @@ impl VmGenId {
117117
"vmgenid: writing new generation ID to guest: {:#034x}",
118118
self.gen_id
119119
);
120+
mem.check_range_plugged(self.guest_address, self.gen_id.to_le_bytes().len())?;
120121
mem.write_slice(&self.gen_id.to_le_bytes(), self.guest_address)
121122
.map_err(VmGenIdError::WriteGuestMemory)?;
122123

src/vmm/src/devices/virtio/mem/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ impl VirtioMem {
510510
updated_range.addr,
511511
self.nb_blocks_to_len(updated_range.nb_blocks),
512512
)
513-
.try_for_each(|slot| {
513+
.try_for_each(|(slot, _)| {
514514
let slot_range = RequestedRange {
515515
addr: slot.guest_addr,
516516
nb_blocks: slot.slice.len() / u64_to_usize(self.config.block_size),

src/vmm/src/vstate/memory.rs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,27 @@ impl GuestRegionMmapExt {
296296
})
297297
}
298298

299+
/// Check whether the given guest address range falls within plugged slots.
300+
pub(crate) fn check_range_plugged(
301+
&self,
302+
caddr: MemoryRegionAddress,
303+
len: usize,
304+
) -> Result<(), GuestMemoryError> {
305+
// caddr is guaranteed to be within the region by the caller
306+
// (try_for_each_region_in_range validates this).
307+
let from = self
308+
.start_addr()
309+
.checked_add(caddr.raw_value())
310+
.expect("caddr should be within the region");
311+
if self
312+
.slots_intersecting_range(from, len)
313+
.any(|(_, plugged)| !plugged)
314+
{
315+
return Err(GuestMemoryError::HostAddressNotAvailable);
316+
}
317+
Ok(())
318+
}
319+
299320
pub(crate) fn slot_cnt(&self) -> u32 {
300321
u32::try_from(u64_to_usize(self.len()) / self.slot_size).unwrap()
301322
}
@@ -347,8 +368,8 @@ impl GuestRegionMmapExt {
347368
&self,
348369
from: GuestAddress,
349370
len: usize,
350-
) -> impl Iterator<Item = GuestMemorySlot<'_>> {
351-
self.slots().map(|(slot, _)| slot).filter(move |slot| {
371+
) -> impl Iterator<Item = (GuestMemorySlot<'_>, bool)> {
372+
self.slots().filter(move |(slot, _)| {
352373
// Two intervals [a, b) and [c, d) intersect iff a < d && c < b.
353374
// This correctly handles the containment case where the slot fully
354375
// contains the range (or vice versa).
@@ -640,6 +661,10 @@ where
640661

641662
/// Discards a memory range, freeing up memory pages
642663
fn discard_range(&self, addr: GuestAddress, range_len: usize) -> Result<(), GuestMemoryError>;
664+
665+
/// Check whether the given guest address range falls entirely within plugged memory.
666+
/// Returns Err if the address is not in any region or is in an unplugged slot.
667+
fn check_range_plugged(&self, addr: GuestAddress, len: usize) -> Result<(), GuestMemoryError>;
643668
}
644669

645670
/// State of a guest memory region saved to file/buffer.
@@ -822,6 +847,12 @@ impl GuestMemoryExtension for GuestMemoryMmap {
822847
region.discard_range(start, len)
823848
})
824849
}
850+
851+
fn check_range_plugged(&self, addr: GuestAddress, len: usize) -> Result<(), GuestMemoryError> {
852+
self.try_for_each_region_in_range(addr, len, |region, offset, chunk_len| {
853+
region.check_range_plugged(offset, chunk_len)
854+
})
855+
}
825856
}
826857

827858
fn create_memfd(
@@ -1520,7 +1551,7 @@ mod tests {
15201551
let from = base.unchecked_add((offset_pages * page_size) as u64);
15211552
let len = len_pages * page_size;
15221553
let found: Vec<_> = region.slots_intersecting_range(from, len).collect();
1523-
let addrs: Vec<_> = found.iter().map(|s| s.guest_addr).collect();
1554+
let addrs: Vec<_> = found.iter().map(|(s, _)| s.guest_addr).collect();
15241555
assert_eq!(
15251556
addrs, expected,
15261557
"offset={offset_pages} pages, len={len_pages} pages"
@@ -1812,4 +1843,51 @@ mod tests {
18121843
}
18131844
}
18141845
}
1846+
1847+
#[test]
1848+
fn test_check_range_plugged() {
1849+
let region_size = 0x4000usize; // 4 slots of 0x1000
1850+
let regions = anonymous(
1851+
vec![(GuestAddress(0x10_0000), region_size)].into_iter(),
1852+
false,
1853+
HugePageConfig::None,
1854+
)
1855+
.unwrap();
1856+
let region = regions.into_iter().next().unwrap();
1857+
1858+
let state = GuestMemoryRegionState {
1859+
base_address: 0x10_0000,
1860+
size: region_size,
1861+
region_type: GuestRegionType::Hotpluggable,
1862+
plugged: vec![true, true, false, true],
1863+
};
1864+
1865+
let ext = GuestRegionMmapExt::from_state(region, &state, 0).unwrap();
1866+
1867+
// Slot 0 (offset 0..0x1000): plugged
1868+
ext.check_range_plugged(MemoryRegionAddress(0), 0x100)
1869+
.unwrap();
1870+
// Slot 1 (offset 0x1000..0x2000): plugged
1871+
ext.check_range_plugged(MemoryRegionAddress(0x1000), 0x100)
1872+
.unwrap();
1873+
// Slot 2 (offset 0x2000..0x3000): unplugged
1874+
assert!(
1875+
ext.check_range_plugged(MemoryRegionAddress(0x2000), 0x100)
1876+
.is_err()
1877+
);
1878+
// Spanning slots 1-2: fails because slot 2 is unplugged
1879+
assert!(
1880+
ext.check_range_plugged(MemoryRegionAddress(0x1800), 0x1000)
1881+
.is_err()
1882+
);
1883+
// Spanning slots 0-1: both plugged
1884+
ext.check_range_plugged(MemoryRegionAddress(0x800), 0x1000)
1885+
.unwrap();
1886+
// Slot 3 (offset 0x3000..0x4000): plugged
1887+
ext.check_range_plugged(MemoryRegionAddress(0x3000), 0x100)
1888+
.unwrap();
1889+
// Zero length: always ok
1890+
ext.check_range_plugged(MemoryRegionAddress(0x2000), 0)
1891+
.unwrap();
1892+
}
18151893
}

tests/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,20 @@ excluding tests marked with `pytest.mark.nonci`):
1717
tools/devtool -y test
1818
```
1919

20+
Note: some performance tests require host performance tuning (e.g. dedicating
21+
some memory for `hugetlbfs`). For those tests, the `--performance` flag is also
22+
required:
23+
24+
```sh
25+
tools/devtool -y test --performance
26+
```
27+
2028
To run only tests from specific directories and/or files:
2129

2230
```sh
2331
tools/devtool -y test -- integration_tests/performance/test_boottime.py
32+
# Or
33+
tools/devtool -y test --performance -- integration_tests/performance/test_huge_pages.py
2434
```
2535

2636
To run a single specific test from a file:
@@ -72,6 +82,9 @@ dev container automatically, so setting them on the host before invoking
7282
at the uVM chroot root. The cheap artifacts (`host-dmesg.log` and the guest
7383
serial console) are always collected. Default off. The nightly performance
7484
pipeline sets this.
85+
- `FC_TEST_DEVELOPMENT_ENVIRONMENT=1` — skip tests that depend on specific host
86+
configurations that don't add any value when running on a development
87+
environment.
7588

7689
### Output
7790

tests/framework/microvm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def __init__(
252252

253253
self._screen_pid = None
254254

255-
self.time_api_requests = global_props.host_linux_version != "6.1"
255+
self.time_api_requests = True
256256
# disable the HTTP API timings as they cause a lot of false positives
257257
if int(os.environ.get("PYTEST_XDIST_WORKER_COUNT", 1)) > 1:
258258
self.time_api_requests = False

tests/framework/properties.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def __init__(self):
8080
self.buildkite_build_number = os.environ.get("BUILDKITE_BUILD_NUMBER")
8181
self.buildkite_pr = os.environ.get("BUILDKITE_PULL_REQUEST", "false") != "false"
8282
self.buildkite_revision_a = os.environ.get("BUILDKITE_PULL_REQUEST_BASE_BRANCH")
83+
# Development environment detection
84+
self.is_dev_env = os.environ.get("FC_TEST_DEVELOPMENT_ENVIRONMENT") == "1"
8385

8486
if self._in_git_repo():
8587
self.git_commit_id = run_cmd("git rev-parse HEAD")

tests/framework/utils_cpuid.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CpuModel(str, Enum):
3838
CPU_DICT = {
3939
CpuVendor.INTEL: {
4040
"Intel(R) Xeon(R) Platinum 8259CL CPU": "INTEL_CASCADELAKE",
41+
"Intel(R) Xeon(R) Platinum 8275CL CPU": "INTEL_CASCADELAKE",
4142
"Intel(R) Xeon(R) Platinum 8375C CPU": "INTEL_ICELAKE",
4243
"Intel(R) Xeon(R) Platinum 8488C": "INTEL_SAPPHIRE_RAPIDS",
4344
"Intel(R) Xeon(R) 6975P-C": "INTEL_GRANITE_RAPIDS",

0 commit comments

Comments
 (0)