diff --git a/.gitignore b/.gitignore index ed2e4ec24..e7612e924 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.vscode /vendor __pycache__ +/.worktrees diff --git a/cloud-hypervisor/src/main.rs b/cloud-hypervisor/src/main.rs index 0f4eb5a60..055c64326 100644 --- a/cloud-hypervisor/src/main.rs +++ b/cloud-hypervisor/src/main.rs @@ -33,8 +33,8 @@ use vmm::vm_config::FwCfgConfig; use vmm::vm_config::IvshmemConfig; use vmm::vm_config::{ BalloonConfig, DeviceConfig, DiskConfig, FsConfig, LandlockConfig, NetConfig, NumaConfig, - PciSegmentConfig, PmemConfig, RateLimiterGroupConfig, TpmConfig, UserDeviceConfig, VdpaConfig, - VmConfig, VsockConfig, + PciSegmentConfig, PmemConfig, RateLimiterGroupConfig, SoundConfig, TpmConfig, UserDeviceConfig, + VdpaConfig, VmConfig, VsockConfig, }; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::signal::block_signal; @@ -424,6 +424,11 @@ fn get_cli_options_sorted( .help("Control serial port: off|null|pty|tty|file=|socket=") .default_value("null") .group("vm-config"), + Arg::new("sound") + .long("sound") + .help(SoundConfig::SYNTAX) + .num_args(1..) + .group("vm-config"), Arg::new("tpm") .long("tpm") .num_args(1) @@ -999,6 +1004,7 @@ mod unit_tests { balloon: None, fs: None, gpu: None, + sound: None, pmem: None, serial: ConsoleConfig { file: None, diff --git a/virtio-devices/src/lib.rs b/virtio-devices/src/lib.rs index a1c7d2e00..ec409b937 100644 --- a/virtio-devices/src/lib.rs +++ b/virtio-devices/src/lib.rs @@ -93,6 +93,8 @@ pub enum ActivateError { VhostUserSetup(#[source] vhost_user::Error), #[error("Failed to setup vhost-user-gpu daemon")] VhostUserGpuSetup(#[source] vhost_user::Error), + #[error("Failed to setup vhost-user-sound daemon")] + VhostUserSoundSetup(#[source] vhost_user::Error), #[error("Failed to create seccomp filter")] CreateSeccompFilter(#[source] seccompiler::Error), #[error("Failed to create rate limiter")] diff --git a/virtio-devices/src/seccomp_filters.rs b/virtio-devices/src/seccomp_filters.rs index dbb6e488d..ac8ec7d0d 100644 --- a/virtio-devices/src/seccomp_filters.rs +++ b/virtio-devices/src/seccomp_filters.rs @@ -25,6 +25,7 @@ pub enum Thread { VirtioVhostBlock, VirtioVhostFs, VirtioVhostGpu, + VirtioVhostSound, VirtioVhostNet, VirtioVhostNetCtl, VirtioVsock, @@ -204,6 +205,18 @@ fn virtio_vhost_gpu_thread_rules() -> Vec<(i64, Vec)> { ] } +fn virtio_vhost_sound_thread_rules() -> Vec<(i64, Vec)> { + vec![ + (libc::SYS_clock_nanosleep, vec![]), + (libc::SYS_connect, vec![]), + (libc::SYS_nanosleep, vec![]), + (libc::SYS_recvmsg, vec![]), + (libc::SYS_sched_setscheduler, vec![]), + (libc::SYS_sendmsg, vec![]), + (libc::SYS_socket, vec![]), + ] +} + fn virtio_vhost_net_ctl_thread_rules() -> Vec<(i64, Vec)> { vec![] } @@ -284,6 +297,7 @@ fn get_seccomp_rules(thread_type: Thread) -> Vec<(i64, Vec)> { Thread::VirtioVhostBlock => virtio_vhost_block_thread_rules(), Thread::VirtioVhostFs => virtio_vhost_fs_thread_rules(), Thread::VirtioVhostGpu => virtio_vhost_gpu_thread_rules(), + Thread::VirtioVhostSound => virtio_vhost_sound_thread_rules(), Thread::VirtioVhostNet => virtio_vhost_net_thread_rules(), Thread::VirtioVhostNetCtl => virtio_vhost_net_ctl_thread_rules(), Thread::VirtioVsock => virtio_vsock_thread_rules(), diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index ac651de94..c83980019 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -37,12 +37,14 @@ pub mod blk; pub mod fs; pub mod gpu; pub mod net; +pub mod sound; pub mod vu_common_ctrl; pub use self::blk::Blk; pub use self::fs::*; pub use self::gpu::Gpu; pub use self::net::Net; +pub use self::sound::Sound; pub use self::vu_common_ctrl::VhostUserConfig; #[derive(Error, Debug)] diff --git a/virtio-devices/src/vhost_user/sound.rs b/virtio-devices/src/vhost_user/sound.rs new file mode 100644 index 000000000..1241e0311 --- /dev/null +++ b/virtio-devices/src/vhost_user/sound.rs @@ -0,0 +1,267 @@ +// Copyright © 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +use std::io; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Barrier, Mutex}; +use std::{result, thread}; + +use event_monitor::event; +use log::{error, warn}; +use seccompiler::SeccompAction; +use vhost::vhost_user::message::{ + VhostUserConfigFlags, VhostUserProtocolFeatures, VhostUserVirtioFeatures, +}; +use vhost::vhost_user::{FrontendReqHandler, VhostUserFrontend, VhostUserFrontendReqHandler}; +use virtio_queue::Queue; +use vm_memory::GuestMemoryAtomic; +use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable}; +use vmm_sys_util::eventfd::EventFd; + +use super::vu_common_ctrl::VhostUserHandle; +use super::{DEFAULT_VIRTIO_FEATURES, Result}; +use crate::seccomp_filters::Thread; +use crate::thread_helper::spawn_virtio_thread; +use crate::vhost_user::VhostUserCommon; +use crate::{ + ActivateResult, GuestMemoryMmap, GuestRegionMmap, VIRTIO_F_IOMMU_PLATFORM, VirtioCommon, + VirtioDevice, VirtioDeviceType, VirtioInterrupt, +}; + +const DEFAULT_QUEUE_NUMBER: usize = 4; +const DEFAULT_QUEUE_SIZE: u16 = 256; + +/// Minimal backend request handler — Sound has no shared memory requirements. +struct SoundBackendReqHandler; +impl VhostUserFrontendReqHandler for SoundBackendReqHandler {} + +pub struct Sound { + common: VirtioCommon, + vu_common: VhostUserCommon, + id: String, + seccomp_action: SeccompAction, + guest_memory: Option>, + epoll_thread: Option>, + exit_evt: EventFd, + iommu: bool, +} + +impl Sound { + /// Create a new vhost-user sound device. + pub fn new( + id: String, + path: &str, + seccomp_action: SeccompAction, + exit_evt: EventFd, + iommu: bool, + ) -> Result { + let num_queues = DEFAULT_QUEUE_NUMBER; + + let mut vu = + VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?; + + let avail_features = DEFAULT_VIRTIO_FEATURES; + + let avail_protocol_features = VhostUserProtocolFeatures::CONFIG + | VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::REPLY_ACK + | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS; + + let (acked_features, acked_protocol_features) = + vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; + + Ok(Sound { + common: VirtioCommon { + device_type: VirtioDeviceType::Sound as u32, + avail_features: acked_features, + acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(), + queue_sizes: vec![DEFAULT_QUEUE_SIZE; num_queues], + paused_sync: Some(Arc::new(Barrier::new(2))), + min_queues: DEFAULT_QUEUE_NUMBER as u16, + paused: Arc::new(AtomicBool::new(false)), + ..Default::default() + }, + vu_common: VhostUserCommon { + vu: Some(Arc::new(Mutex::new(vu))), + acked_protocol_features, + socket_path: path.to_string(), + vu_num_queues: num_queues, + ..Default::default() + }, + id, + seccomp_action, + guest_memory: None, + epoll_thread: None, + exit_evt, + iommu, + }) + } +} + +impl Drop for Sound { + fn drop(&mut self) { + if let Some(kill_evt) = self.common.kill_evt.take() { + let _ = kill_evt.write(1); + } + self.common.wait_for_epoll_threads(); + if let Some(thread) = self.epoll_thread.take() + && let Err(e) = thread.join() + { + error!("Error joining thread: {e:?}"); + } + } +} + +impl VirtioDevice for Sound { + fn device_type(&self) -> u32 { + self.common.device_type + } + + fn queue_max_sizes(&self) -> &[u16] { + &self.common.queue_sizes + } + + fn features(&self) -> u64 { + let mut features = self.common.avail_features; + if self.iommu { + features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; + } + features + } + + fn ack_features(&mut self, value: u64) { + self.common.ack_features(value); + } + + fn read_config(&self, offset: u64, mut data: &mut [u8]) { + if let Some(vu) = &self.vu_common.vu + && let Err(e) = vu + .lock() + .unwrap() + .socket_handle() + .get_config( + offset as u32, + data.len() as u32, + VhostUserConfigFlags::WRITABLE, + data, + ) + .map_err(|e| format!("{e:?}")) + .and_then(|(_, config)| { + use std::io::Write; + data.write_all(&config).map_err(|e| format!("{e:?}")) + }) + { + error!("Failed getting vhost-user-sound configuration: {e:?}"); + } + } + + fn activate( + &mut self, + mem: GuestMemoryAtomic, + interrupt_cb: Arc, + queues: Vec<(usize, Queue, EventFd)>, + ) -> ActivateResult { + self.common.activate(&queues, interrupt_cb.clone())?; + self.guest_memory = Some(mem.clone()); + + let (kill_evt, pause_evt) = self.common.dup_eventfds(); + + let mut handler = self.vu_common.activate( + mem, + &queues, + interrupt_cb, + self.common.acked_features, + None::>, + kill_evt, + pause_evt, + )?; + + let paused = self.common.paused.clone(); + let paused_sync = self.common.paused_sync.clone(); + + let mut epoll_threads = Vec::new(); + spawn_virtio_thread( + &self.id, + &self.seccomp_action, + Thread::VirtioVhostSound, + &mut epoll_threads, + &self.exit_evt, + move || { + let param = libc::sched_param { sched_priority: 88 }; + // SAFETY: pthread_self() always returns a valid handle for the + // calling thread, and param is initialized above. + let ret = unsafe { + libc::pthread_setschedparam(libc::pthread_self(), libc::SCHED_FIFO, ¶m) + }; + if ret != 0 { + warn!( + "Failed to set SCHED_FIFO for sound thread: {}", + io::Error::from_raw_os_error(ret) + ); + } + handler.run(&paused, paused_sync.as_ref().unwrap()) + }, + )?; + self.epoll_thread = Some(epoll_threads.remove(0)); + + event!("virtio-device", "activated", "id", &self.id); + Ok(()) + } + + fn reset(&mut self) -> Option> { + if self.common.pause_evt.take().is_some() { + self.common.resume().ok()?; + } + + if let Some(vu) = &self.vu_common.vu + && let Err(e) = vu.lock().unwrap().reset_vhost_user() + { + error!("Failed to reset vhost-user daemon: {e:?}"); + return None; + } + + if let Some(kill_evt) = self.common.kill_evt.take() { + let _ = kill_evt.write(1); + } + + event!("virtio-device", "reset", "id", &self.id); + + Some(self.common.interrupt_cb.take().unwrap()) + } + + fn shutdown(&mut self) { + self.vu_common.shutdown(); + } + + fn add_memory_region( + &mut self, + region: &Arc, + ) -> std::result::Result<(), crate::Error> { + self.vu_common.add_memory_region(&self.guest_memory, region) + } +} + +impl Pausable for Sound { + fn pause(&mut self) -> result::Result<(), MigratableError> { + self.vu_common.pause()?; + self.common.pause() + } + + fn resume(&mut self) -> result::Result<(), MigratableError> { + self.common.resume()?; + + if let Some(epoll_thread) = &self.epoll_thread { + epoll_thread.thread().unpark(); + } + + self.vu_common.resume() + } +} + +impl Snapshottable for Sound { + fn id(&self) -> String { + self.id.clone() + } +} +impl Transportable for Sound {} +impl Migratable for Sound {} diff --git a/vm-virtio/src/lib.rs b/vm-virtio/src/lib.rs index b7f537070..04a6cc9ee 100644 --- a/vm-virtio/src/lib.rs +++ b/vm-virtio/src/lib.rs @@ -37,6 +37,7 @@ pub enum VirtioDeviceType { Vsock = 19, Iommu = 23, Mem = 24, + Sound = 25, Fs = 26, Pmem = 27, Watchdog = 35, // Temporary until official number allocated @@ -57,6 +58,7 @@ impl From for VirtioDeviceType { 19 => VirtioDeviceType::Vsock, 23 => VirtioDeviceType::Iommu, 24 => VirtioDeviceType::Mem, + 25 => VirtioDeviceType::Sound, 26 => VirtioDeviceType::Fs, 27 => VirtioDeviceType::Pmem, 35 => VirtioDeviceType::Watchdog, @@ -82,6 +84,7 @@ impl fmt::Display for VirtioDeviceType { VirtioDeviceType::Vsock => "vsock", VirtioDeviceType::Iommu => "iommu", VirtioDeviceType::Mem => "mem", + VirtioDeviceType::Sound => "sound", VirtioDeviceType::Fs => "fs", VirtioDeviceType::Pmem => "pmem", VirtioDeviceType::Watchdog => "watchdog", diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 7011dc50e..810f70eb8 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -619,6 +619,10 @@ components: type: array items: $ref: "#/components/schemas/FsConfig" + sound: + type: array + items: + $ref: "#/components/schemas/SoundConfig" pmem: type: array items: @@ -1135,6 +1139,19 @@ components: id: type: string + SoundConfig: + required: + - socket + type: object + properties: + socket: + type: string + pci_segment: + type: integer + format: int16 + id: + type: string + PmemConfig: required: - file diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 1216b0cd4..0d49491c5 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -96,6 +96,12 @@ pub enum Error { /// Error parsing GPU parameters #[error("Error parsing --gpu")] ParseGpu(#[source] OptionParserError), + /// Sound socket is missing + #[error("Error parsing --sound: socket missing")] + ParseSoundSockMissing, + /// Error parsing Sound parameters + #[error("Error parsing --sound")] + ParseSound(#[source] OptionParserError), /// Error parsing persistent memory parameters #[error("Error parsing --pmem")] ParsePersistentMemory(#[source] OptionParserError), @@ -400,6 +406,7 @@ pub struct VmParams<'a> { pub balloon: Option<&'a str>, pub fs: Option>, pub gpu: Option>, + pub sound: Option>, pub pmem: Option>, pub serial: &'a str, pub console: &'a str, @@ -464,6 +471,9 @@ impl<'a> VmParams<'a> { let gpu: Option> = args .get_many::("gpu") .map(|x| x.map(|y| y as &str).collect()); + let sound: Option> = args + .get_many::("sound") + .map(|x| x.map(|y| y as &str).collect()); let pmem: Option> = args .get_many::("pmem") .map(|x| x.map(|y| y as &str).collect()); @@ -519,6 +529,7 @@ impl<'a> VmParams<'a> { balloon, fs, gpu, + sound, pmem, serial, console, @@ -1755,6 +1766,49 @@ impl GpuConfig { } } +impl SoundConfig { + pub const SYNTAX: &'static str = "virtio-sound parameters \ + \"socket=,id=,pci_segment=\""; + + pub fn parse(sound: &str) -> Result { + let mut parser = OptionParser::new(); + parser.add("socket").add("id").add("pci_segment"); + parser.parse(sound).map_err(Error::ParseSound)?; + + let socket = PathBuf::from(parser.get("socket").ok_or(Error::ParseSoundSockMissing)?); + let id = parser.get("id"); + + let pci_segment = parser + .convert("pci_segment") + .map_err(Error::ParseSound)? + .unwrap_or_default(); + + Ok(SoundConfig { + socket, + id, + pci_segment, + }) + } + + pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> { + if let Some(platform_config) = vm_config.platform.as_ref() { + if self.pci_segment >= platform_config.num_pci_segments { + return Err(ValidationError::InvalidPciSegment(self.pci_segment)); + } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() + && iommu_segments.contains(&self.pci_segment) + { + return Err(ValidationError::IommuNotSupportedOnSegment( + self.pci_segment, + )); + } + } + + Ok(()) + } +} + #[cfg(feature = "fw_cfg")] impl FwCfgConfig { pub const SYNTAX: &'static str = "Boot params to pass to FW CFG device \ @@ -2784,6 +2838,13 @@ impl VmConfig { } } + if let Some(sounds) = &self.sound { + for sound in sounds { + sound.validate(self)?; + Self::validate_identifier(&mut id_list, &sound.id)?; + } + } + if let Some(pmems) = &self.pmem { for pmem in pmems { pmem.validate(self)?; @@ -3046,6 +3107,15 @@ impl VmConfig { gpu = Some(gpu_config_list); } + let mut sound: Option> = None; + if let Some(sound_list) = &vm_params.sound { + let mut sound_config_list = Vec::new(); + for item in sound_list.iter() { + sound_config_list.push(SoundConfig::parse(item)?); + } + sound = Some(sound_config_list); + } + let mut pmem: Option> = None; if let Some(pmem_list) = &vm_params.pmem { let mut pmem_config_list = Vec::new(); @@ -3183,6 +3253,7 @@ impl VmConfig { balloon, fs, gpu, + sound, pmem, serial, console, @@ -3251,6 +3322,13 @@ impl VmConfig { removed |= gpu_list.len() != len; } + // Remove if sound device + if let Some(sound_list) = self.sound.as_mut() { + let len = sound_list.len(); + sound_list.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id)); + removed |= sound_list.len() != len; + } + // Remove if net device if let Some(net) = self.net.as_mut() { let len = net.len(); @@ -3324,6 +3402,7 @@ impl Clone for VmConfig { pvmemcontrol: self.pvmemcontrol.clone(), fs: self.fs.clone(), gpu: self.gpu.clone(), + sound: self.sound.clone(), pmem: self.pmem.clone(), serial: self.serial.clone(), console: self.console.clone(), @@ -4233,6 +4312,7 @@ mod unit_tests { balloon: None, fs: None, gpu: None, + sound: None, pmem: None, serial: default_serial(), console: default_console(), @@ -4437,6 +4517,7 @@ mod unit_tests { balloon: None, fs: None, gpu: None, + sound: None, pmem: None, serial: ConsoleConfig { file: None, diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index abd0aff14..0bf4448f0 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -128,8 +128,8 @@ use crate::serial_manager::{Error as SerialManagerError, SerialManager}; use crate::vm_config::IvshmemConfig; use crate::vm_config::{ ConsoleOutputMode, DEFAULT_IOMMU_ADDRESS_WIDTH_BITS, DEFAULT_PCI_SEGMENT_APERTURE_WEIGHT, - DeviceConfig, DiskConfig, FsConfig, GpuConfig, NetConfig, PmemConfig, UserDeviceConfig, - VdpaConfig, VhostMode, VmConfig, VsockConfig, + DeviceConfig, DiskConfig, FsConfig, GpuConfig, NetConfig, PmemConfig, SoundConfig, + UserDeviceConfig, VdpaConfig, VhostMode, VmConfig, VsockConfig, }; use crate::{DEVICE_MANAGER_SNAPSHOT_ID, GuestRegionMmap, PciDeviceInfo, device_node}; @@ -159,6 +159,7 @@ const IVSHMEM_DEVICE_NAME: &str = "__ivshmem"; const DISK_DEVICE_NAME_PREFIX: &str = "_disk"; const FS_DEVICE_NAME_PREFIX: &str = "_fs"; const GPU_DEVICE_NAME_PREFIX: &str = "_gpu"; +const SOUND_DEVICE_NAME_PREFIX: &str = "_sound"; const NET_DEVICE_NAME_PREFIX: &str = "_net"; const PMEM_DEVICE_NAME_PREFIX: &str = "_pmem"; const VDPA_DEVICE_NAME_PREFIX: &str = "_vdpa"; @@ -219,6 +220,14 @@ pub enum DeviceManagerError { #[error("Cannot find a memory range for virtio-gpu")] GpuRangeAllocation, + /// Cannot create virtio-sound device + #[error("Cannot create virtio-sound device")] + CreateVirtioSound(#[source] virtio_devices::vhost_user::Error), + + /// Virtio-sound device was created without a socket. + #[error("Virtio-sound device was created without a socket")] + NoVirtioSoundSock, + /// Cannot create vhost-user-blk device #[error("Cannot create vhost-user-blk device")] CreateVhostUserBlk(#[source] virtio_devices::vhost_user::Error), @@ -2570,6 +2579,9 @@ impl DeviceManager { // Add virtio-gpu if required self.make_virtio_gpu_devices()?; + // Add virtio-sound if required + self.make_virtio_sound_devices()?; + // Add virtio-pmem if required self.make_virtio_pmem_devices()?; @@ -3280,6 +3292,64 @@ impl DeviceManager { Ok(()) } + fn make_virtio_sound_device( + &mut self, + sound_cfg: &mut SoundConfig, + ) -> DeviceManagerResult { + let id = if let Some(id) = &sound_cfg.id { + id.clone() + } else { + let id = self.next_device_name(SOUND_DEVICE_NAME_PREFIX)?; + sound_cfg.id = Some(id.clone()); + id + }; + + info!("Creating virtio-sound device: {sound_cfg:?}"); + + let node = device_node!(id); + + if let Some(sound_socket) = sound_cfg.socket.to_str() { + let virtio_sound_device = virtio_devices::vhost_user::Sound::new( + id.clone(), + sound_socket, + self.seccomp_action.clone(), + self.exit_evt + .try_clone() + .map_err(DeviceManagerError::EventFd)?, + self.force_iommu, + ) + .map_err(DeviceManagerError::CreateVirtioSound)?; + + let virtio_sound_device = Arc::new(Mutex::new(virtio_sound_device)); + + self.device_tree.lock().unwrap().insert(id.clone(), node); + + Ok(MetaVirtioDevice { + virtio_device: Arc::clone(&virtio_sound_device) + as Arc>, + iommu: false, + id, + pci_segment: sound_cfg.pci_segment, + dma_handler: None, + }) + } else { + Err(DeviceManagerError::NoVirtioSoundSock) + } + } + + fn make_virtio_sound_devices(&mut self) -> DeviceManagerResult<()> { + let mut sound_devices = self.config.lock().unwrap().sound.take(); + if let Some(sound_list_cfg) = &mut sound_devices { + for sound_cfg in sound_list_cfg.iter_mut() { + let device = self.make_virtio_sound_device(sound_cfg)?; + self.virtio_devices.push(device); + } + } + self.config.lock().unwrap().sound = sound_devices; + + Ok(()) + } + fn make_virtio_pmem_device( &mut self, pmem_cfg: &mut PmemConfig, diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 78ea74fb0..146e1fc1c 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -2480,6 +2480,7 @@ mod unit_tests { balloon: None, fs: None, gpu: None, + sound: None, pmem: None, serial: ConsoleConfig { file: None, diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index bb40b99f6..2d07f8cf3 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -657,6 +657,7 @@ fn vmm_thread_rules( (libc::SYS_rt_sigreturn, vec![]), (libc::SYS_sched_getaffinity, vec![]), (libc::SYS_sched_setaffinity, vec![]), + (libc::SYS_sched_setscheduler, vec![]), (libc::SYS_sched_yield, vec![]), (libc::SYS_seccomp, vec![]), (libc::SYS_sendmsg, vec![]), diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 4052d935d..60ae513d0 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -485,6 +485,22 @@ impl ApplyLandlock for GpuConfig { } } +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct SoundConfig { + pub socket: PathBuf, + #[serde(default)] + pub id: Option, + #[serde(default)] + pub pci_segment: u16, +} + +impl ApplyLandlock for SoundConfig { + fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> { + landlock.add_rule_with_access(&self.socket, "rw")?; + Ok(()) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct PmemConfig { pub file: PathBuf, @@ -939,6 +955,7 @@ pub struct VmConfig { pub balloon: Option, pub fs: Option>, pub gpu: Option>, + pub sound: Option>, pub pmem: Option>, #[serde(default = "default_serial")] pub serial: ConsoleConfig, @@ -1020,6 +1037,12 @@ impl VmConfig { } } + if let Some(sound_configs) = &self.sound { + for sound_config in sound_configs.iter() { + sound_config.apply_landlock(&mut landlock)?; + } + } + if let Some(pmem_configs) = &self.pmem { for pmem_config in pmem_configs.iter() { pmem_config.apply_landlock(&mut landlock)?;