Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
03e5061
Expose memory mapping; Add optional memfile dump
ValentaTomas Nov 15, 2025
eff49ed
Fix compile errors
ValentaTomas Nov 15, 2025
5cfd4e3
Remove required field from spec
ValentaTomas Dec 3, 2025
523dc12
Fix parameter
ValentaTomas Dec 5, 2025
8769de7
Fix type
ValentaTomas Dec 5, 2025
55b6479
Add upload script
ValentaTomas Dec 6, 2025
1133bd6
Parse without python
ValentaTomas Dec 9, 2025
0bb99c5
Refactor
ValentaTomas Dec 10, 2025
01f3491
Add tests
ValentaTomas Dec 18, 2025
178223a
Fix version
ValentaTomas Dec 18, 2025
dbc5552
fix(test_reboot): do not assert thread count
Manciukic Apr 30, 2025
f1510d5
Limit test suite
ValentaTomas Dec 19, 2025
4129956
Merge branch 'firecracker-v1.12' into firecracker-v1.12-direct-mem
ValentaTomas Dec 19, 2025
8a5491a
Tweak GHA
ValentaTomas Dec 19, 2025
63ed770
Merge branch 'firecracker-v1.12-direct-mem' of https://github.com/e2b…
ValentaTomas Dec 19, 2025
0111337
Change runner
ValentaTomas Dec 19, 2025
6726338
Remove flaky test invocation
ValentaTomas Dec 21, 2025
671febe
Fix build script
ValentaTomas Dec 21, 2025
6ffeddf
Switch to 7 hash chars everywhere
ValentaTomas Dec 21, 2025
1dfe77c
Remove g
ValentaTomas Dec 21, 2025
717921c
Cleanup
ValentaTomas Dec 22, 2025
54a1c1a
api: implement API for dirty memory
bchalios Feb 18, 2026
8fc760f
feat: enable write-protection on guest memory
bchalios Feb 18, 2026
a41d3fb
ci: remove dependency changes test
bchalios Feb 14, 2026
827b783
doc(swagger): add /memory/dirty endpoint definition
bchalios Feb 18, 2026
59b0ad1
doc(swagger): make mem_file_path optional in SnapshotCreateParams
bchalios Feb 18, 2026
210cbac
fix(kvmclock): don't pass KVM_CLOCK_REALTIME to KVM_SET_CLOCK
bchalios Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .github/workflows/dependency_modification_check.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test_results/*
/resources/linux
/resources/x86_64
/resources/aarch64
.env
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gcloud 534.0.0
rust 1.85.0
29 changes: 26 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-include .env

.PHONY: build
build:
./scripts/build.sh

.PHONY: upload
upload:
./scripts/upload.sh $(GCP_PROJECT_ID)

.PHONY: build-and-upload
build-and-upload: build upload
4 changes: 4 additions & 0 deletions resources/seccomp/aarch64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
"syscall": "madvise",
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
},
{
"syscall": "mincore",
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
},
{
"syscall": "mmap",
"comment": "Used by the VirtIO balloon device",
Expand Down
14 changes: 11 additions & 3 deletions resources/seccomp/x86_64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@
"syscall": "madvise",
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
},
{
"syscall": "mincore",
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
},
{
"syscall": "pread64",
"comment": "Used by get_dirty_memory to read /proc/self/pagemap entries"
},
{
"syscall": "mmap",
"comment": "Used by the VirtIO balloon device",
Expand Down Expand Up @@ -524,8 +532,8 @@
"comment": "sigaltstack is used by Rust stdlib to remove alternative signal stack during thread teardown."
},
{
"syscall": "getrandom",
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
"syscall": "getrandom",
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
},
{
"syscall": "accept4",
Expand Down Expand Up @@ -1152,4 +1160,4 @@
}
]
}
}
}
17 changes: 17 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

set -euo pipefail

# The format will be: v<major>.<minor>.<patch>_<commit_hash> — e.g. v1.7.2_8bb88311
# Extract full version from src/firecracker/swagger/firecracker.yaml
FC_VERSION=$(awk '/^info:/{flag=1} flag && /^ version:/{print $2; exit}' src/firecracker/swagger/firecracker.yaml)
commit_hash=$(git rev-parse --short=7 HEAD)
version_name="v${FC_VERSION}_${commit_hash}"
echo "Version name: $version_name"

echo "Starting to build Firecracker version: $version_name"
tools/devtool -y build --release

mkdir -p "./build/fc/${version_name}"
cp ./build/cargo_target/x86_64-unknown-linux-musl/release/firecracker "./build/fc/${version_name}/firecracker"
echo "Finished building Firecracker version: $version_name and copied to ./build/fc/${version_name}/firecracker"
13 changes: 13 additions & 0 deletions scripts/upload.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

set -euo pipefail

GCP_PROJECT_ID=$1

gsutil -h "Cache-Control:no-cache, max-age=0" cp -r "build/fc/*" "gs://${GCP_PROJECT_ID}-fc-versions"
if [ "$GCP_PROJECT_ID" == "e2b-prod" ]; then
# Upload kernel to GCP public builds bucket
gsutil -h "Cache-Control:no-cache, max-age=0" cp -r "build/fc/*" "gs://${GCP_PROJECT_ID}-public-builds/firecrackers/"
fi

rm -rf build/fc/*
1 change: 1 addition & 0 deletions src/cpu-template-helper/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub fn build_microvm_from_config(
state: VmState::NotStarted,
vmm_version: CPU_TEMPLATE_HELPER_VERSION.to_string(),
app_name: "cpu-template-helper".to_string(),
memory_regions: None,
};
let mut vm_resources =
VmResources::from_json(&config, &instance_info, HTTP_MAX_PAYLOAD_SIZE, None)
Expand Down
4 changes: 2 additions & 2 deletions src/firecracker/src/api_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand All @@ -288,7 +288,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand Down
70 changes: 70 additions & 0 deletions src/firecracker/src/api_server/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use super::request::logger::parse_put_logger;
use super::request::machine_configuration::{
parse_get_machine_config, parse_patch_machine_config, parse_put_machine_config,
};
use super::request::memory::{parse_get_memory, parse_get_memory_dirty, parse_get_memory_mappings};
use super::request::metrics::parse_put_metrics;
use super::request::mmds::{parse_get_mmds, parse_patch_mmds, parse_put_mmds};
use super::request::net::{parse_patch_net, parse_put_net};
Expand Down Expand Up @@ -82,6 +83,15 @@ impl TryFrom<&Request> for ParsedRequest {
Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
}
(Method::Get, "machine-config", None) => parse_get_machine_config(),
(Method::Get, "memory", None) => match path_tokens.next() {
Some("mappings") => parse_get_memory_mappings(),
Some("dirty") => parse_get_memory_dirty(),
None => parse_get_memory(),
_ => Err(RequestError::InvalidPathMethod(
request_uri.to_string(),
Method::Get,
)),
},
(Method::Get, "mmds", None) => parse_get_mmds(),
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
Expand Down Expand Up @@ -172,6 +182,9 @@ impl ParsedRequest {
}
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
VmmData::MemoryMappings(mappings) => Self::success_response_with_data(mappings),
VmmData::Memory(memory) => Self::success_response_with_data(memory),
VmmData::MemoryDirty(dirty) => Self::success_response_with_data(dirty),
VmmData::VmmVersion(version) => Self::success_response_with_data(
&serde_json::json!({ "firecracker_version": version.as_str() }),
),
Expand Down Expand Up @@ -568,6 +581,15 @@ pub mod tests {
VmmData::InstanceInformation(info) => {
http_response(&serde_json::to_string(info).unwrap(), 200)
}
VmmData::MemoryMappings(mappings) => {
http_response(&serde_json::to_string(mappings).unwrap(), 200)
}
VmmData::Memory(memory) => {
http_response(&serde_json::to_string(memory).unwrap(), 200)
}
VmmData::MemoryDirty(dirty) => {
http_response(&serde_json::to_string(dirty).unwrap(), 200)
}
VmmData::VmmVersion(version) => http_response(
&serde_json::json!({ "firecracker_version": version.as_str() }).to_string(),
200,
Expand All @@ -589,6 +611,18 @@ pub mod tests {
verify_ok_response_with(VmmData::MachineConfiguration(MachineConfig::default()));
verify_ok_response_with(VmmData::MmdsValue(serde_json::from_str("{}").unwrap()));
verify_ok_response_with(VmmData::InstanceInformation(InstanceInfo::default()));
verify_ok_response_with(VmmData::MemoryMappings(
vmm::vmm_config::instance_info::MemoryMappingsResponse { mappings: vec![] },
));
verify_ok_response_with(VmmData::Memory(
vmm::vmm_config::instance_info::MemoryResponse {
resident: vec![],
empty: vec![],
},
));
verify_ok_response_with(VmmData::MemoryDirty(
vmm::vmm_config::instance_info::MemoryDirty { bitmap: vec![] },
));
verify_ok_response_with(VmmData::VmmVersion(String::default()));

// Error.
Expand Down Expand Up @@ -662,6 +696,42 @@ pub mod tests {
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_memory_mappings() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/memory/mappings", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_memory() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/memory", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_memory_dirty() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/memory/dirty", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_version() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
Expand Down
52 changes: 52 additions & 0 deletions src/firecracker/src/api_server/request/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use vmm::logger::{IncMetric, METRICS};
use vmm::rpc_interface::VmmAction;

use super::super::parsed_request::{ParsedRequest, RequestError};

pub(crate) fn parse_get_memory_mappings() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.instance_info_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryMappings))
}

pub(crate) fn parse_get_memory() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.instance_info_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemory))
}

pub(crate) fn parse_get_memory_dirty() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.instance_info_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryDirty))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::api_server::parsed_request::RequestAction;

#[test]
fn test_parse_get_memory_mappings_request() {
match parse_get_memory_mappings().unwrap().into_parts() {
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemoryMappings => {}
_ => panic!("Test failed."),
}
}

#[test]
fn test_parse_get_memory_request() {
match parse_get_memory().unwrap().into_parts() {
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemory => {}
_ => panic!("Test failed."),
}
}

#[test]
fn test_parse_get_memory_dirty_request() {
match parse_get_memory_dirty().unwrap().into_parts() {
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemoryDirty => {}
_ => panic!("Test failed."),
}
}
}
1 change: 1 addition & 0 deletions src/firecracker/src/api_server/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod entropy;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
pub mod memory;
pub mod metrics;
pub mod mmds;
pub mod net;
Expand Down
4 changes: 2 additions & 2 deletions src/firecracker/src/api_server/request/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand All @@ -154,7 +154,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Full,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand Down
1 change: 1 addition & 0 deletions src/firecracker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ fn main_exec() -> Result<(), MainError> {
state: VmState::NotStarted,
vmm_version: FIRECRACKER_VERSION.to_string(),
app_name: "Firecracker".to_string(),
memory_regions: None,
};

if let Some(metrics_path) = arguments.single_value("metrics-path") {
Expand Down
Loading
Loading