Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
192 changes: 101 additions & 91 deletions docs/device-api.md

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion resources/guest_configs/ci.config
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ CONFIG_SERIO_I8042=y
CONFIG_SERIO_LIBPS2=y
CONFIG_SERIO_GSCPS2=y
CONFIG_KEYBOARD_ATKBD=y
CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_KEYBOARD=y

# virtio-mem support
CONFIG_STRICT_DEVMEM=y
CONFIG_VIRTIO_MEM=y
13 changes: 13 additions & 0 deletions src/firecracker/src/api_server/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ use super::request::net::{parse_patch_net, parse_put_net};
use super::request::snapshot::{parse_patch_vm_state, parse_put_snapshot};
use super::request::version::parse_get_version;
use super::request::vsock::parse_put_vsock;
use crate::api_server::request::hotplug::memory::{
parse_get_memory_hotplug, parse_put_memory_hotplug,
};
use crate::api_server::request::serial::parse_put_serial;

#[derive(Debug)]
Expand Down Expand Up @@ -84,6 +87,9 @@ impl TryFrom<&Request> for ParsedRequest {
}
(Method::Get, "machine-config", None) => parse_get_machine_config(),
(Method::Get, "mmds", None) => parse_get_mmds(),
(Method::Get, "hotplug", None) if path_tokens.next() == Some("memory") => {
parse_get_memory_hotplug()
}
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
(Method::Put, "balloon", Some(body)) => parse_put_balloon(body),
Expand All @@ -101,6 +107,9 @@ impl TryFrom<&Request> for ParsedRequest {
(Method::Put, "snapshot", Some(body)) => parse_put_snapshot(body, path_tokens.next()),
(Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
(Method::Put, "entropy", Some(body)) => parse_put_entropy(body),
(Method::Put, "hotplug", Some(body)) if path_tokens.next() == Some("memory") => {
parse_put_memory_hotplug(body)
}
(Method::Put, _, None) => method_to_error(Method::Put),
(Method::Patch, "balloon", Some(body)) => parse_patch_balloon(body, path_tokens.next()),
(Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.next()),
Expand Down Expand Up @@ -173,6 +182,7 @@ impl ParsedRequest {
Self::success_response_with_data(balloon_config)
}
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
VmmData::VirtioMemStatus(data) => Self::success_response_with_data(data),
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
VmmData::VmmVersion(version) => Self::success_response_with_data(
&serde_json::json!({ "firecracker_version": version.as_str() }),
Expand Down Expand Up @@ -557,6 +567,9 @@ pub mod tests {
VmmData::BalloonStats(stats) => {
http_response(&serde_json::to_string(stats).unwrap(), 200)
}
VmmData::VirtioMemStatus(data) => {
http_response(&serde_json::to_string(data).unwrap(), 200)
}
VmmData::Empty => http_response("", 204),
VmmData::FullVmConfig(cfg) => {
http_response(&serde_json::to_string(cfg).unwrap(), 200)
Expand Down
82 changes: 82 additions & 0 deletions src/firecracker/src/api_server/request/hotplug/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use micro_http::Body;
use vmm::logger::{IncMetric, METRICS};
use vmm::rpc_interface::VmmAction;
use vmm::vmm_config::memory_hotplug::MemoryHotplugConfig;

use crate::api_server::parsed_request::{ParsedRequest, RequestError};

pub(crate) fn parse_put_memory_hotplug(body: &Body) -> Result<ParsedRequest, RequestError> {
METRICS.put_api_requests.hotplug_memory_count.inc();
let config = serde_json::from_slice::<MemoryHotplugConfig>(body.raw()).inspect_err(|_| {
METRICS.put_api_requests.hotplug_memory_fails.inc();
})?;
Ok(ParsedRequest::new_sync(VmmAction::SetMemoryHotplugDevice(
config,
)))
}

pub(crate) fn parse_get_memory_hotplug() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.hotplug_memory_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryHotplugStatus))
}

#[cfg(test)]
mod tests {
use vmm::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_BLOCK_SIZE, VIRTIO_MEM_DEFAULT_SLOT_SIZE};
use vmm::utils::bytes_to_mib;

use super::*;
use crate::api_server::parsed_request::tests::vmm_action_from_request;

#[test]
fn test_parse_put_memory_hotplug_request() {
parse_put_memory_hotplug(&Body::new("invalid_payload")).unwrap_err();

// PUT with invalid fields.
let body = r#"{
"total_size_mib": "bar"
}"#;
parse_put_memory_hotplug(&Body::new(body)).unwrap_err();

// PUT with valid input fields with defaults.
let body = r#"{
"total_size_mib": 2048
}"#;
let expected_config = MemoryHotplugConfig {
total_size_mib: 2048,
block_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_BLOCK_SIZE),
slot_size_mib: bytes_to_mib(VIRTIO_MEM_DEFAULT_SLOT_SIZE),
};
assert_eq!(
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
VmmAction::SetMemoryHotplugDevice(expected_config)
);

// PUT with valid input fields.
let body = r#"{
"total_size_mib": 2048,
"block_size_mib": 64,
"slot_size_mib": 64
}"#;
let expected_config = MemoryHotplugConfig {
total_size_mib: 2048,
block_size_mib: 64,
slot_size_mib: 64,
};
assert_eq!(
vmm_action_from_request(parse_put_memory_hotplug(&Body::new(body)).unwrap()),
VmmAction::SetMemoryHotplugDevice(expected_config)
);
}

#[test]
fn test_parse_parse_get_memory_hotplug_request() {
assert_eq!(
vmm_action_from_request(parse_get_memory_hotplug().unwrap()),
VmmAction::GetMemoryHotplugStatus
);
}
}
4 changes: 4 additions & 0 deletions src/firecracker/src/api_server/request/hotplug/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod memory;
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 @@ -7,6 +7,7 @@ pub mod boot_source;
pub mod cpu_configuration;
pub mod drive;
pub mod entropy;
pub mod hotplug;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
Expand Down
79 changes: 79 additions & 0 deletions src/firecracker/swagger/firecracker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,43 @@ paths:
schema:
$ref: "#/definitions/Error"

/hotplug/memory:
put:
summary: Configures the hotpluggable memory
operationId: putMemoryHotplug
description:
Configure the hotpluggable memory, which is a virtio-mem device, with an associated memory area
that can be hot(un)plugged in the guest on demand using the PATCH API.
parameters:
- name: body
in: body
description: Hotpluggable memory configuration
required: true
schema:
$ref: "#/definitions/MemoryHotplugConfig"
responses:
204:
description: Hotpluggable memory configured
default:
description: Internal server error
schema:
$ref: "#/definitions/Error"
get:
summary: Retrieves the status of the hotpluggable memory
operationId: getMemoryHotplug
description:
Reuturn the status of the hotpluggable memory. This can be used to follow the progress of the guest
after a PATCH API.
responses:
200:
description: OK
schema:
$ref: "#/definitions/MemoryHotplugStatus"
default:
description: Internal server error
schema:
$ref: "#/definitions/Error"

/network-interfaces/{iface_id}:
put:
summary: Creates a network interface. Pre-boot only.
Expand Down Expand Up @@ -1364,6 +1401,48 @@ definitions:
type: string
description: Path to a file or named pipe on the host to which serial output should be written.

MemoryHotplugConfig:
type: object
description:
The configuration of the hotpluggable memory device (virtio-mem)
properties:
total_size_mib:
type: integer
description: Total size of the hotpluggable memory in MiB.
slot_size_mib:
type: integer
default: 128
minimum: 128
description: Slot size for the hotpluggable memory in MiB. This will determine the granularity of
hot-plug memory from the host. Refer to the device documentation on how to tune this value.
block_size_mib:
type: integer
default: 2
minimum: 2
description: (Logical) Block size for the hotpluggable memory in MiB. This will determine the logical
granularity of hot-plug memory for the guest. Refer to the device documentation on how to tune this value.

MemoryHotplugStatus:
type: object
description:
The status of the hotpluggable memory device (virtio-mem)
properties:
total_size_mib:
type: integer
description: Total size of the hotpluggable memory in MiB.
slot_size_mib:
type: integer
description: Slot size for the hotpluggable memory in MiB.
block_size_mib:
type: integer
description: (Logical) Block size for the hotpluggable memory in MiB.
plugged_size_mib:
type: integer
description: Plugged size for the hotpluggable memory in MiB.
requested_size_mib:
type: integer
description: Requested size for the hotpluggable memory in MiB.

FirecrackerVersion:
type: object
description:
Expand Down
16 changes: 8 additions & 8 deletions src/vmm/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ pub use aarch64::vm::{ArchVm, ArchVmError, VmState};
pub use aarch64::{
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
initrd_load_addr, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE,
layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM, layout::GSI_LEGACY_START, layout::GSI_MSI_END,
layout::GSI_MSI_NUM, layout::GSI_MSI_START, layout::MEM_32BIT_DEVICES_SIZE,
layout::MEM_32BIT_DEVICES_START, layout::MEM_64BIT_DEVICES_SIZE,
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
layout::FIRST_ADDR_PAST_64BITS_MMIO, layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM,
layout::GSI_LEGACY_START, layout::GSI_MSI_END, layout::GSI_MSI_NUM, layout::GSI_MSI_START,
layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
layout::MMIO32_MEM_START, layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, layout::RTC_MEM_START, layout::SERIAL_MEM_START,
layout::SPI_START, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
};
Expand All @@ -46,9 +46,9 @@ pub use x86_64::vm::{ArchVm, ArchVmError, VmState};
pub use crate::arch::x86_64::{
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
initrd_load_addr, layout::APIC_ADDR, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE,
layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM, layout::GSI_LEGACY_START, layout::GSI_MSI_END,
layout::GSI_MSI_NUM, layout::GSI_MSI_START, layout::IOAPIC_ADDR,
layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
layout::FIRST_ADDR_PAST_64BITS_MMIO, layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM,
layout::GSI_LEGACY_START, layout::GSI_MSI_END, layout::GSI_MSI_NUM, layout::GSI_MSI_START,
layout::IOAPIC_ADDR, layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
layout::MMIO32_MEM_START, layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START,
Expand Down
74 changes: 74 additions & 0 deletions src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::device_manager::{
use crate::devices::acpi::vmgenid::VmGenIdError;
use crate::devices::virtio::balloon::Balloon;
use crate::devices::virtio::block::device::Block;
use crate::devices::virtio::mem::VirtioMem;
use crate::devices::virtio::net::Net;
use crate::devices::virtio::rng::Entropy;
use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
Expand All @@ -45,6 +46,7 @@ use crate::seccomp::BpfThreadMap;
use crate::snapshot::Persist;
use crate::vmm_config::instance_info::InstanceInfo;
use crate::vmm_config::machine_config::MachineConfigError;
use crate::vmm_config::memory_hotplug::MemoryHotplugConfig;
use crate::vstate::kvm::{Kvm, KvmError};
use crate::vstate::memory::GuestRegionMmap;
#[cfg(target_arch = "aarch64")]
Expand Down Expand Up @@ -252,6 +254,17 @@ pub fn build_microvm_for_boot(
)?;
}

// Attach virtio-mem device if configured
if let Some(memory_hotplug) = &vm_resources.memory_hotplug {
attach_virtio_mem_device(
&mut device_manager,
&vm,
&mut boot_cmdline,
memory_hotplug,
event_manager,
)?;
}

#[cfg(target_arch = "aarch64")]
device_manager.attach_legacy_devices_aarch64(
&vm,
Expand Down Expand Up @@ -576,6 +589,29 @@ fn attach_entropy_device(
device_manager.attach_virtio_device(vm, id, entropy_device.clone(), cmdline, false)
}

fn attach_virtio_mem_device(
device_manager: &mut DeviceManager,
vm: &Arc<Vm>,
cmdline: &mut LoaderKernelCmdline,
config: &MemoryHotplugConfig,
event_manager: &mut EventManager,
) -> Result<(), StartMicrovmError> {
let virtio_mem = Arc::new(Mutex::new(
VirtioMem::new(
Arc::clone(vm),
config.total_size_mib,
config.block_size_mib,
config.slot_size_mib,
)
.map_err(|e| StartMicrovmError::Internal(VmmError::VirtioMem(e)))?,
));

let id = virtio_mem.lock().expect("Poisoned lock").id().to_string();
event_manager.add_subscriber(virtio_mem.clone());
device_manager.attach_virtio_device(vm, id, virtio_mem.clone(), cmdline, false)?;
Ok(())
}

fn attach_block_devices<'a, I: Iterator<Item = &'a Arc<Mutex<Block>>> + Debug>(
device_manager: &mut DeviceManager,
vm: &Arc<Vm>,
Expand Down Expand Up @@ -1200,4 +1236,42 @@ pub(crate) mod tests {
"virtio_mmio.device=4K@0xc0001000:5"
));
}

pub(crate) fn insert_virtio_mem_device(
vmm: &mut Vmm,
cmdline: &mut Cmdline,
event_manager: &mut EventManager,
config: MemoryHotplugConfig,
) {
attach_virtio_mem_device(
&mut vmm.device_manager,
&vmm.vm,
cmdline,
&config,
event_manager,
)
.unwrap();
}

#[test]
fn test_attach_virtio_mem_device() {
let mut event_manager = EventManager::new().expect("Unable to create EventManager");
let mut vmm = default_vmm();

let config = MemoryHotplugConfig {
total_size_mib: 1024,
block_size_mib: 2,
slot_size_mib: 128,
};

let mut cmdline = default_kernel_cmdline();
insert_virtio_mem_device(&mut vmm, &mut cmdline, &mut event_manager, config);

// Check if the vsock device is described in kernel_cmdline.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
assert!(cmdline_contains(
&cmdline,
"virtio_mmio.device=4K@0xc0001000:5"
));
}
}
Loading