virtio-devices: balloon: Enable use with confidential VMs

Following the pattern used by the existing virtio devices make the
balloon device work with confidential VMs (e.g. SEV-SNP). This requires
advertising the VIRTIO_F_ACCESS_PLATFORM feature. Do not expose this to
the user as a controllable option and instead only enable in on the
"force" case.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-04-17 09:33:10 +01:00
parent 5bd6fdc17d
commit 12dd72d88f
3 changed files with 31 additions and 4 deletions

View File

@@ -49,6 +49,7 @@ fuzz_target!(|bytes: &[u8]| -> Corpus {
BALLOON_SIZE, BALLOON_SIZE,
true, true,
true, true,
false,
SeccompAction::Allow, SeccompAction::Allow,
EventFd::new(EFD_NONBLOCK).unwrap(), EventFd::new(EFD_NONBLOCK).unwrap(),
None, None,

View File

@@ -34,14 +34,15 @@ use vm_memory::{
GuestMemoryError, GuestMemoryRegion, GuestMemoryError, GuestMemoryRegion,
}; };
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_virtio::{AccessPlatform, Translatable};
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use crate::seccomp_filters::Thread; use crate::seccomp_filters::Thread;
use crate::thread_helper::spawn_virtio_thread; use crate::thread_helper::spawn_virtio_thread;
use crate::{ use crate::{
ActivateResult, EPOLL_HELPER_EVENT_LAST, EpollHelper, EpollHelperError, EpollHelperHandler, ActivateResult, EPOLL_HELPER_EVENT_LAST, EpollHelper, EpollHelperError, EpollHelperHandler,
GuestMemoryMmap, VIRTIO_F_VERSION_1, VirtioCommon, VirtioDevice, VirtioDeviceType, GuestMemoryMmap, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VirtioCommon, VirtioDevice,
VirtioInterrupt, VirtioInterruptType, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
}; };
const QUEUE_SIZE: u16 = 128; const QUEUE_SIZE: u16 = 128;
@@ -160,6 +161,7 @@ struct BalloonEpollHandler {
kill_evt: EventFd, kill_evt: EventFd,
pause_evt: EventFd, pause_evt: EventFd,
pbp: Option<PartiallyBalloonedPage>, pbp: Option<PartiallyBalloonedPage>,
access_platform: Option<Arc<dyn AccessPlatform>>,
} }
impl BalloonEpollHandler { impl BalloonEpollHandler {
@@ -277,7 +279,12 @@ impl BalloonEpollHandler {
let mut offset = 0u64; let mut offset = 0u64;
while offset < desc.len() as u64 { while offset < desc.len() as u64 {
let addr = desc.addr().checked_add(offset).unwrap(); let addr = desc
.addr()
.checked_add(offset)
.unwrap()
.translate_gva(self.access_platform.as_deref(), data_chunk_size)
.map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?;
let pfn: u32 = desc_chain let pfn: u32 = desc_chain
.memory() .memory()
.read_obj(addr) .read_obj(addr)
@@ -324,7 +331,11 @@ impl BalloonEpollHandler {
let mut descs_len = 0; let mut descs_len = 0;
while let Some(desc) = desc_chain.next() { while let Some(desc) = desc_chain.next() {
descs_len += desc.len(); descs_len += desc.len();
Self::release_memory_range(desc_chain.memory(), desc.addr(), desc.len() as usize)?; let addr = desc
.addr()
.translate_gva(self.access_platform.as_deref(), desc.len() as usize)
.map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?;
Self::release_memory_range(desc_chain.memory(), addr, desc.len() as usize)?;
} }
self.queues[queue_index] self.queues[queue_index]
@@ -437,11 +448,13 @@ pub struct Balloon {
impl Balloon { impl Balloon {
// Create a new virtio-balloon. // Create a new virtio-balloon.
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
id: String, id: String,
size: u64, size: u64,
deflate_on_oom: bool, deflate_on_oom: bool,
free_page_reporting: bool, free_page_reporting: bool,
access_platform_enabled: bool,
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
exit_evt: EventFd, exit_evt: EventFd,
state: Option<BalloonState>, state: Option<BalloonState>,
@@ -464,6 +477,9 @@ impl Balloon {
if free_page_reporting { if free_page_reporting {
avail_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING; avail_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING;
} }
if access_platform_enabled {
avail_features |= 1u64 << VIRTIO_F_ACCESS_PLATFORM;
}
let config = VirtioBalloonConfig { let config = VirtioBalloonConfig {
num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32, num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
@@ -628,6 +644,7 @@ impl VirtioDevice for Balloon {
kill_evt, kill_evt,
pause_evt, pause_evt,
pbp: None, pbp: None,
access_platform: self.common.access_platform(),
}; };
let paused = self.common.paused.clone(); let paused = self.common.paused.clone();
@@ -648,6 +665,14 @@ impl VirtioDevice for Balloon {
Ok(()) Ok(())
} }
fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
self.common.set_access_platform(access_platform);
}
fn access_platform(&self) -> Option<Arc<dyn AccessPlatform>> {
self.common.access_platform()
}
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> { fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
let result = self.common.reset(); let result = self.common.reset();
event!("virtio-device", "reset", "id", &self.id); event!("virtio-device", "reset", "id", &self.id);

View File

@@ -3704,6 +3704,7 @@ impl DeviceManager {
balloon_config.size, balloon_config.size,
balloon_config.deflate_on_oom, balloon_config.deflate_on_oom,
balloon_config.free_page_reporting, balloon_config.free_page_reporting,
self.force_access_platform,
self.seccomp_action.clone(), self.seccomp_action.clone(),
self.exit_evt self.exit_evt
.try_clone() .try_clone()