From 1aff0ebcadbdea207b05a1a0d6ba27a0ed039b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= Date: Mon, 15 Jun 2026 19:36:11 +0200 Subject: [PATCH] runtime-rs: qemu: don't set slots/maxmem for confidential guests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Smp::new already omits the CPU-hotplug parameter (maxcpus) for confidential guests, but Memory::new unconditionally emitted the memory-hotplug parameters (slots/maxmem) regardless of confidential_guest. Confidential guests (TDX/SEV-SNP) don't support memory hotplug. Setting maxmem reserves a device-memory (hotplug) region which, once guest RAM exceeds 2 GiB and gets split across the 32-bit PCI hole, breaks early VM boot: the guest hangs before producing any console output. This is why Guaranteed QoS pods (default memory + the pod's memory request, pushing total RAM above 2 GiB) failed to boot on TDX/SNP with runtime-rs, while BestEffort/Burstable pods (staying at the 2048 MiB default) booted fine. Stop emitting slots/maxmem when confidential_guest is set, so no memory-hotplug region is reserved, mirroring Smp::new and the Go runtime's memoryTopology(). Note this only affects the QEMU command line; the runtime-side memory resize path is left untouched. Signed-off-by: Fabiano FidĂȘncio Assisted-by: Cursor --- .../hypervisor/src/qemu/cmdline_generator.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs index 7fc991d107..cf73034933 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs @@ -263,13 +263,24 @@ struct Memory { impl Memory { fn new(config: &HypervisorConfig) -> Memory { let mem_size = config.memory_info.default_memory as u64; - let max_mem_size = config.memory_info.default_maxmemory as u64; + + // Don't reserve a memory-hotplug region (slots/maxmem) for confidential + // guests, which don't support memory hotplug, mirroring how Smp::new + // omits maxcpus and what the Go runtime does in memoryTopology(). + let (num_slots, max_mem_size) = if config.security_info.confidential_guest { + (0, 0) + } else { + ( + config.memory_info.memory_slots, + config.memory_info.default_maxmemory as u64, + ) + }; // Memory sizes are given in megabytes in configuration.toml so we // need to convert them to bytes for storage. Memory { size: mem_size * MI_B, - num_slots: config.memory_info.memory_slots, + num_slots, max_size: max_mem_size * MI_B, memory_backend_file: None, }