From 986f54cd6c39d1f49de3a1bb93a4bbfb326f459c Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 7 Aug 2024 15:45:06 +0200 Subject: [PATCH] arch: implement SHM region management We have multiple devices (fs and gpu) that can use SHM regions. So far, we were creating a single SHM region so only one device could make use of it. In this commit, we implement SHM region management so multiple devices can request their own regions. A perhaps controversial decision is to NOT use the GuestMemory abstraction to manage the SHM regions, and instead operate them naked. The reason behind this is that GuestMemory is primarily designed with the intention of self-managing the memory mappings, which is exactly the opposite of what we need to do with SHM regions. Also, SHM regions operate in very different ways on Linux/KVM and macOS/HVF. On the first, we need to create a large mapping, register it with KVM, and then lay SHM windows on top of it as nested mappings. On the latter, nested mappings aren't supported, so each SHM window must be explicitly registered with HVF, so we don't need to create nor register an initial large mapping. In addition to this, SHM windows on Linux/KVM operate with offsets on top of the host address, while macOS/HVF operate with offsets on top of the guest address. For the moment, we're hardcoding the SHM region sizes for gpu and fs. A future commit will extend the API so users can configure those sizes as desired. Signed-off-by: Sergio Lopez --- src/arch/src/aarch64/mod.rs | 17 +++----- src/arch/src/lib.rs | 17 ++++++-- src/arch/src/shm.rs | 43 ++++++++++++++++++++ src/arch/src/x86_64/mod.rs | 21 +++++----- src/devices/src/virtio/device.rs | 1 + src/devices/src/virtio/fs/server.rs | 12 +++++- src/devices/src/virtio/gpu/worker.rs | 3 +- src/vmm/src/builder.rs | 60 ++++++++++++++++++---------- src/vmm/src/lib.rs | 13 ++++-- src/vmm/src/linux/vstate.rs | 52 ++++++++++++++++++++++-- src/vmm/src/macos/vstate.rs | 14 ++++++- 11 files changed, 196 insertions(+), 57 deletions(-) create mode 100644 src/arch/src/shm.rs diff --git a/src/arch/src/aarch64/mod.rs b/src/arch/src/aarch64/mod.rs index 9450b94d..c76d99a9 100644 --- a/src/arch/src/aarch64/mod.rs +++ b/src/arch/src/aarch64/mod.rs @@ -16,12 +16,11 @@ pub mod macos; #[cfg(target_os = "macos")] pub use self::macos::*; -use std::cmp::min; use std::collections::HashMap; use std::fmt::Debug; use self::gic::GICDevice; -use crate::ArchMemoryInfo; +use crate::{round_up, ArchMemoryInfo}; use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap}; #[cfg(feature = "efi")] @@ -42,8 +41,6 @@ pub enum Error { /// The start of the memory area reserved for MMIO devices. pub const MMIO_MEM_START: u64 = layout::MAPPED_IO_START; -/// The size of the MMIO shared memory area used by virtio-fs DAX. -pub const MMIO_SHM_SIZE: u64 = 1 << 33; pub use self::fdt::DeviceInfoForFDT; use crate::DeviceType; @@ -51,26 +48,24 @@ use crate::DeviceType; /// Returns a Vec of the valid memory addresses for aarch64. /// See [`layout`](layout) module for a drawing of the specific memory model for this platform. pub fn arch_memory_regions(size: usize) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) { - let dram_size = min(size as u64, layout::DRAM_MEM_MAX_SIZE) as usize; + let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }; + let dram_size = round_up(size, page_size); let ram_last_addr = layout::DRAM_MEM_START + (dram_size as u64); let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000; + let info = ArchMemoryInfo { ram_last_addr, shm_start_addr, - shm_size: MMIO_SHM_SIZE, + page_size, }; let regions = if cfg!(feature = "efi") { vec![ // Space for loading EDK2 and its variables (GuestAddress(0u64), 0x800_0000), (GuestAddress(layout::DRAM_MEM_START), dram_size), - (GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize), ] } else { - vec![ - (GuestAddress(layout::DRAM_MEM_START), dram_size), - (GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize), - ] + vec![(GuestAddress(layout::DRAM_MEM_START), dram_size)] }; (info, regions) diff --git a/src/arch/src/lib.rs b/src/arch/src/lib.rs index f09be292..7e8e24dc 100644 --- a/src/arch/src/lib.rs +++ b/src/arch/src/lib.rs @@ -11,9 +11,12 @@ use std::result; pub struct ArchMemoryInfo { pub ram_last_addr: u64, pub shm_start_addr: u64, - pub shm_size: u64, + pub page_size: usize, } +pub mod shm; +pub use shm::*; + /// Module for aarch64 related functionality. #[cfg(target_arch = "aarch64")] pub mod aarch64; @@ -22,7 +25,6 @@ pub mod aarch64; pub use aarch64::{ arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, MMIO_MEM_START, - MMIO_SHM_SIZE, }; /// Module for x86_64 related functionality. @@ -33,7 +35,7 @@ pub mod x86_64; pub use crate::x86_64::{ arch_memory_regions, configure_system, get_kernel_start, initrd_load_addr, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, Error, BIOS_SIZE, BIOS_START, - MMIO_MEM_START, MMIO_SHM_SIZE, RESET_VECTOR, + MMIO_MEM_START, RESET_VECTOR, }; /// Type for returning public functions outcome. @@ -66,6 +68,15 @@ pub struct InitrdConfig { /// Default (smallest) memory page size for the supported architectures. pub const PAGE_SIZE: usize = 4096; +pub fn round_up(size: usize, align: usize) -> usize { + let page_mask = align - 1; + (size + page_mask) & !page_mask +} +pub fn round_down(size: usize, align: usize) -> usize { + let page_mask = !(align - 1); + size & page_mask +} + impl fmt::Display for DeviceType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") diff --git a/src/arch/src/shm.rs b/src/arch/src/shm.rs new file mode 100644 index 00000000..e1f14061 --- /dev/null +++ b/src/arch/src/shm.rs @@ -0,0 +1,43 @@ +use crate::round_up; + +use super::ArchMemoryInfo; +#[derive(Debug)] +pub enum Error { + OutOfSpace, +} + +#[derive(Clone)] +pub struct ShmRegion { + pub guest_addr: u64, + pub size: usize, +} + +pub struct ShmManager { + next_guest_addr: u64, + page_size: usize, +} + +impl ShmManager { + pub fn new(info: &ArchMemoryInfo) -> ShmManager { + Self { + next_guest_addr: info.shm_start_addr, + page_size: info.page_size, + } + } + + pub fn get_region(&mut self, size: usize) -> Result { + let size = round_up(size, self.page_size); + + let region = ShmRegion { + guest_addr: self.next_guest_addr, + size, + }; + + if let Some(addr) = self.next_guest_addr.checked_add(size as u64) { + self.next_guest_addr = addr; + Ok(region) + } else { + Err(Error::OutOfSpace) + } + } +} diff --git a/src/arch/src/x86_64/mod.rs b/src/arch/src/x86_64/mod.rs index 64dc6113..e31046e8 100644 --- a/src/arch/src/x86_64/mod.rs +++ b/src/arch/src/x86_64/mod.rs @@ -17,8 +17,7 @@ pub mod msr; /// Logic for configuring x86_64 registers. pub mod regs; -use crate::ArchMemoryInfo; -use crate::InitrdConfig; +use crate::{round_up, ArchMemoryInfo, InitrdConfig}; use arch_gen::x86::bootparam::{boot_params, E820_RAM}; use vm_memory::Bytes; use vm_memory::{ @@ -60,8 +59,6 @@ const FIRST_ADDR_PAST_32BITS: u64 = 1 << 32; const MEM_32BIT_GAP_SIZE: u64 = 768 << 20; /// The start of the memory area reserved for MMIO devices. pub const MMIO_MEM_START: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE; -/// The size of the MMIO shared memory area used by virtio-fs DAX. -pub const MMIO_SHM_SIZE: u64 = 1 << 33; /// Returns a Vec of the valid memory addresses. /// These should be used to configure the GuestMemoryMmap structure for the platform. @@ -73,6 +70,9 @@ pub fn arch_memory_regions( kernel_load_addr: u64, kernel_size: usize, ) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) { + let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }; + + let size = round_up(size, page_size); if size < (kernel_load_addr + kernel_size as u64) as usize { panic!("Kernel doesn't fit in RAM"); } @@ -90,7 +90,6 @@ pub fn arch_memory_regions( vec![ (GuestAddress(0), kernel_load_addr as usize), (GuestAddress(kernel_load_addr + kernel_size as u64), size), - (GuestAddress(FIRST_ADDR_PAST_32BITS), MMIO_SHM_SIZE as usize), ], ) } @@ -108,7 +107,6 @@ pub fn arch_memory_regions( (MMIO_MEM_START - (kernel_load_addr + kernel_size as u64)) as usize, ), (GuestAddress(FIRST_ADDR_PAST_32BITS), remaining), - (GuestAddress(shm_start_addr), MMIO_SHM_SIZE as usize), ], ) } @@ -116,7 +114,7 @@ pub fn arch_memory_regions( let info = ArchMemoryInfo { ram_last_addr, shm_start_addr, - shm_size: MMIO_SHM_SIZE, + page_size, }; (info, regions) } @@ -132,6 +130,9 @@ pub fn arch_memory_regions( kernel_load_addr: u64, kernel_size: usize, ) -> (ArchMemoryInfo, Vec<(GuestAddress, usize)>) { + let page_size: usize = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }; + + let size = round_up(size, page_size); if size < (kernel_load_addr + kernel_size as u64) as usize { panic!("Kernel doesn't fit in RAM"); } @@ -170,7 +171,7 @@ pub fn arch_memory_regions( let info = ArchMemoryInfo { ram_last_addr, shm_start_addr, - shm_size: 0, + page_size, }; (info, regions) } @@ -319,7 +320,7 @@ mod tests { #[test] fn regions_lt_4gb() { let (_info, regions) = arch_memory_regions(1usize << 29, KERNEL_LOAD_ADDR, KERNEL_SIZE); - assert_eq!(3, regions.len()); + assert_eq!(2, regions.len()); assert_eq!(GuestAddress(0), regions[0].0); assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1); assert_eq!( @@ -333,7 +334,7 @@ mod tests { fn regions_gt_4gb() { let (_info, regions) = arch_memory_regions((1usize << 32) + 0x8000, KERNEL_LOAD_ADDR, KERNEL_SIZE); - assert_eq!(4, regions.len()); + assert_eq!(3, regions.len()); assert_eq!(GuestAddress(0), regions[0].0); assert_eq!(KERNEL_LOAD_ADDR as usize, regions[0].1); assert_eq!( diff --git a/src/devices/src/virtio/device.rs b/src/devices/src/virtio/device.rs index 242ba83a..8eb90b6d 100644 --- a/src/devices/src/virtio/device.rs +++ b/src/devices/src/virtio/device.rs @@ -21,6 +21,7 @@ pub enum DeviceState { #[derive(Clone)] pub struct VirtioShmRegion { + #[cfg(target_os = "linux")] pub host_addr: u64, pub guest_addr: u64, pub size: usize, diff --git a/src/devices/src/virtio/fs/server.rs b/src/devices/src/virtio/fs/server.rs index 8f91e693..b97f6641 100644 --- a/src/devices/src/virtio/fs/server.rs +++ b/src/devices/src/virtio/fs/server.rs @@ -137,11 +137,19 @@ impl Server { x if x == Opcode::CopyFileRange as u32 => self.copyfilerange(in_header, r, w), x if (x == Opcode::SetupMapping as u32) && shm_region.is_some() => { let shm = shm_region.unwrap(); - self.setupmapping(in_header, r, w, shm.host_addr, shm.size as u64) + #[cfg(target_os = "linux")] + let shm_base_addr = shm.host_addr; + #[cfg(target_os = "macos")] + let shm_base_addr = shm.guest_addr; + self.setupmapping(in_header, r, w, shm_base_addr, shm.size as u64) } x if (x == Opcode::RemoveMapping as u32) && shm_region.is_some() => { let shm = shm_region.unwrap(); - self.removemapping(in_header, r, w, shm.host_addr, shm.size as u64) + #[cfg(target_os = "linux")] + let shm_base_addr = shm.host_addr; + #[cfg(target_os = "macos")] + let shm_base_addr = shm.guest_addr; + self.removemapping(in_header, r, w, shm_base_addr, shm.size as u64) } _ => reply_error( linux_error(io::Error::from_raw_os_error(libc::ENOSYS)), diff --git a/src/devices/src/virtio/gpu/worker.rs b/src/devices/src/virtio/gpu/worker.rs index 5597d055..16c15ca2 100644 --- a/src/devices/src/virtio/gpu/worker.rs +++ b/src/devices/src/virtio/gpu/worker.rs @@ -16,7 +16,7 @@ use utils::eventfd::EventFd; use vm_memory::{GuestAddress, GuestMemoryMmap}; use super::super::descriptor_utils::{Reader, Writer}; -use super::super::{GpuError, Queue as VirtQueue, VirtioShmRegion, VIRTIO_MMIO_INT_VRING}; +use super::super::{GpuError, Queue as VirtQueue, VIRTIO_MMIO_INT_VRING}; use super::protocol::{ virtio_gpu_ctrl_hdr, virtio_gpu_mem_entry, GpuCommand, GpuResponse, VirtioGpuResult, }; @@ -24,6 +24,7 @@ use super::virtio_gpu::VirtioGpu; use crate::legacy::Gic; use crate::virtio::gpu::protocol::{VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_FLAG_INFO_RING_IDX}; use crate::virtio::gpu::virtio_gpu::VirtioGpuRing; +use crate::virtio::VirtioShmRegion; use crate::Error as DeviceError; pub struct Worker { diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 1f5d993e..c8ca3ef6 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -23,8 +23,6 @@ use devices::legacy::VcpuList; use devices::legacy::{Gic, Serial}; #[cfg(feature = "net")] use devices::virtio::Net; -#[cfg(not(feature = "tee"))] -use devices::virtio::VirtioShmRegion; use devices::virtio::{port_io, MmioTransport, PortDescription, Vsock}; #[cfg(target_os = "macos")] use hvf::MemoryMapping; @@ -55,6 +53,8 @@ use crate::vstate::{Error as VstateError, Vcpu, VcpuConfig, Vm}; use arch::ArchMemoryInfo; #[cfg(feature = "tee")] use arch::InitrdConfig; +#[cfg(not(feature = "tee"))] +use arch::ShmManager; #[cfg(feature = "tee")] use kvm_bindings::KVM_MAX_CPUID_ENTRIES; use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; @@ -67,6 +67,7 @@ use vm_memory::mmap::GuestRegionMmap; use vm_memory::mmap::MmapRegion; #[cfg(any(target_arch = "aarch64", feature = "tee"))] use vm_memory::Bytes; +#[cfg(feature = "tee")] use vm_memory::GuestMemory; use vm_memory::{GuestAddress, GuestMemoryMmap}; @@ -131,7 +132,10 @@ pub enum StartMicrovmError { SecureVirtAttest(VstateError), /// Cannot initialize the Secure Virtualization backend. SecureVirtPrepare(VstateError), - + /// Error adding an SHM region. + ShmAdd(VstateError), + /// Error configuring an SHM region. + ShmCreate(arch::shm::Error), /// The TEE specified is not supported. InvalidTee, } @@ -295,6 +299,18 @@ impl Display for StartMicrovmError { "Cannot initialize the Secure Virtualization backend. {err_msg}" ) } + ShmAdd(ref err) => { + let mut err_msg = format!("{:?}", err); + err_msg = err_msg.replace('\"', ""); + + write!(f, "Error while adding an SHM region. {err_msg}") + } + ShmCreate(ref err) => { + let mut err_msg = format!("{:?}", err); + err_msg = err_msg.replace('\"', ""); + + write!(f, "Error while creating an SHM region. {err_msg}") + } InvalidTee => { write!(f, "TEE selected is not currently supported") } @@ -560,13 +576,7 @@ pub fn build_microvm( } #[cfg(not(feature = "tee"))] - let _shm_region = Some(VirtioShmRegion { - host_addr: guest_memory - .get_host_address(GuestAddress(arch_memory_info.shm_start_addr)) - .unwrap() as u64, - guest_addr: arch_memory_info.shm_start_addr, - size: arch_memory_info.shm_size as usize, - }); + let mut shm_manager = ShmManager::new(&arch_memory_info); let mut vmm = Vmm { guest_memory, @@ -596,7 +606,7 @@ pub fn build_microvm( attach_gpu_device( &mut vmm, event_manager, - _shm_region, + &mut shm_manager, intc.clone(), virgl_flags, #[cfg(target_os = "macos")] @@ -604,7 +614,7 @@ pub fn build_microvm( )?; } #[cfg(not(feature = "tee"))] - attach_fs_devices(&mut vmm, &vm_resources.fs, None, intc.clone())?; + attach_fs_devices(&mut vmm, &vm_resources.fs, &mut shm_manager, intc.clone())?; #[cfg(feature = "blk")] attach_block_devices(&mut vmm, &vm_resources.block, intc.clone())?; if let Some(vsock) = vm_resources.vsock.get() { @@ -1111,7 +1121,7 @@ fn attach_mmio_device( fn attach_fs_devices( vmm: &mut Vmm, fs_devs: &FsBuilder, - shm_region: Option, + shm_manager: &mut ShmManager, intc: Option>>, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1123,9 +1133,13 @@ fn attach_fs_devices( fs.lock().unwrap().set_intc(intc.clone()); } - if let Some(ref shm) = shm_region { - fs.lock().unwrap().set_shm_region(shm.clone()); - } + let shm_region = shm_manager + .get_region(1 << 30) + .map_err(StartMicrovmError::ShmCreate)?; + let virtio_shm_region = vmm + .add_shm_region(shm_region) + .map_err(StartMicrovmError::ShmAdd)?; + fs.lock().unwrap().set_shm_region(virtio_shm_region); // The device mutex mustn't be locked here otherwise it will deadlock. attach_mmio_device( @@ -1379,7 +1393,7 @@ fn attach_rng_device( fn attach_gpu_device( vmm: &mut Vmm, event_manager: &mut EventManager, - shm_region: Option, + shm_manager: &mut ShmManager, intc: Option>>, virgl_flags: u32, #[cfg(target_os = "macos")] map_sender: Sender, @@ -1405,9 +1419,13 @@ fn attach_gpu_device( gpu.lock().unwrap().set_intc(intc); } - if let Some(ref shm) = shm_region { - gpu.lock().unwrap().set_shm_region(shm.clone()); - } + let shm_region = shm_manager + .get_region(1 << 33) + .map_err(StartMicrovmError::ShmCreate)?; + let virtio_shm_region = vmm + .add_shm_region(shm_region) + .map_err(StartMicrovmError::ShmAdd)?; + gpu.lock().unwrap().set_shm_region(virtio_shm_region); // The device mutex mustn't be locked here otherwise it will deadlock. attach_mmio_device(vmm, id, MmioTransport::new(vmm.guest_memory().clone(), gpu)) @@ -1487,7 +1505,7 @@ pub mod tests { #[test] #[cfg(target_arch = "aarch64")] fn test_create_vcpus_aarch64() { - let guest_memory = create_guest_memory(128).unwrap(); + let (guest_memory, _info) = default_guest_memory(128).unwrap(); let vm = setup_vm(&guest_memory).unwrap(); let vcpu_count = 2; diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index ea3fdb38..fe3194dd 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -52,12 +52,10 @@ use crate::terminal::term_set_canonical_mode; use crate::vstate::VcpuEvent; use crate::vstate::{Vcpu, VcpuHandle, VcpuResponse, Vm}; -use arch::ArchMemoryInfo; -use arch::DeviceType; -use arch::InitrdConfig; +use arch::{ArchMemoryInfo, DeviceType, InitrdConfig, ShmRegion}; #[cfg(target_os = "macos")] use crossbeam_channel::Sender; -use devices::virtio::VmmExitObserver; +use devices::virtio::{VirtioShmRegion, VmmExitObserver}; use devices::BusDevice; use kernel::cmdline::Cmdline as KernelCmdline; use polly::event_manager::{self, EventManager, Subscriber}; @@ -362,6 +360,13 @@ impl Vmm { &self.vm } + pub fn add_shm_region( + &mut self, + region: ShmRegion, + ) -> std::result::Result { + self.vm.add_shm_region(region) + } + #[cfg(target_os = "macos")] pub fn add_mapping( &self, diff --git a/src/vmm/src/linux/vstate.rs b/src/vmm/src/linux/vstate.rs index 8a64740f..564ad9b8 100644 --- a/src/vmm/src/linux/vstate.rs +++ b/src/vmm/src/linux/vstate.rs @@ -6,6 +6,7 @@ // found in the THIRD-PARTY file. use crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError}; +use devices::virtio::VirtioShmRegion; use libc::{c_int, c_void, siginfo_t}; use std::cell::Cell; use std::fmt::{Display, Formatter}; @@ -34,9 +35,9 @@ use kbs_types::Tee; #[cfg(feature = "tee")] use crate::resources::TeeConfig; use crate::vmm_config::machine_config::CpuFeaturesTemplate; -use arch; #[cfg(target_arch = "aarch64")] use arch::aarch64::gic::GICDevice; +use arch::{self, ShmRegion}; #[cfg(target_arch = "x86_64")] use cpuid::{c3, filter_cpuid, t2, VmSpec}; #[cfg(target_arch = "x86_64")] @@ -111,6 +112,8 @@ pub enum Error { SetupGIC(arch::aarch64::gic::Error), /// Cannot set the memory regions. SetUserMemoryRegion(kvm_ioctls::Error), + /// Error creating memory map for SHM region. + ShmMmap(io::Error), #[cfg(feature = "amd-sev")] /// Error initializing the Secure Virtualization Backend (SEV). SevSecVirtInit(SevError), @@ -270,6 +273,7 @@ impl Display for Error { "Cannot set the local interruption due to bad configuration: {e:?}" ), SetUserMemoryRegion(e) => write!(f, "Cannot set the memory regions: {e}"), + ShmMmap(e) => write!(f, "Error creating memory map for SHM region: {e}"), #[cfg(feature = "tee")] SevSecVirtInit(e) => { write!( @@ -453,6 +457,7 @@ impl KvmContext { /// A wrapper around creating and using a VM. pub struct Vm { fd: VmFd, + next_mem_slot: u32, // X86 specific fields. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -492,6 +497,7 @@ impl Vm { Ok(Vm { fd: vm_fd, + next_mem_slot: 0, #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] supported_cpuid, #[cfg(target_arch = "x86_64")] @@ -524,6 +530,7 @@ impl Vm { Ok(Vm { fd: vm_fd, + next_mem_slot: 0, supported_cpuid, supported_msrs, sev, @@ -553,12 +560,12 @@ impl Vm { if guest_mem.num_regions() > kvm_max_memslots { return Err(Error::NotEnoughMemorySlots); } - for (index, region) in guest_mem.iter().enumerate() { + for region in guest_mem.iter() { // It's safe to unwrap because the guest address is valid. let host_addr = guest_mem.get_host_address(region.start_addr()).unwrap(); - info!("Guest memory starts at {:x?}", host_addr); + debug!("Guest memory starts at {:x?}", host_addr); let memory_region = kvm_userspace_memory_region { - slot: index as u32, + slot: self.next_mem_slot, guest_phys_addr: region.start_addr().raw_value(), memory_size: region.len(), userspace_addr: host_addr as u64, @@ -571,6 +578,7 @@ impl Vm { .set_user_memory_region(memory_region) .map_err(Error::SetUserMemoryRegion)?; }; + self.next_mem_slot += 1; } #[cfg(target_arch = "x86_64")] @@ -581,6 +589,42 @@ impl Vm { Ok(()) } + pub fn add_shm_region(&mut self, region: ShmRegion) -> Result { + let host_addr = unsafe { + libc::mmap( + std::ptr::null_mut(), + region.size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED | libc::MAP_ANONYMOUS, + -1, + 0 as libc::off_t, + ) + }; + if host_addr == libc::MAP_FAILED { + return Err(Error::ShmMmap(io::Error::last_os_error())); + } + let memory_region = kvm_userspace_memory_region { + slot: self.next_mem_slot, + guest_phys_addr: region.guest_addr, + memory_size: region.size as u64, + userspace_addr: host_addr as u64, + flags: 0, + }; + // Safe because we mapped the memory region, we made sure that the regions + // are not overlapping. + unsafe { + self.fd + .set_user_memory_region(memory_region) + .map_err(Error::SetUserMemoryRegion)?; + }; + self.next_mem_slot += 1; + Ok(VirtioShmRegion { + host_addr: host_addr as u64, + guest_addr: region.guest_addr, + size: region.size, + }) + } + #[cfg(feature = "amd-sev")] pub fn sev_secure_virt_prepare( &mut self, diff --git a/src/vmm/src/macos/vstate.rs b/src/vmm/src/macos/vstate.rs index 3e89621e..0b725511 100644 --- a/src/vmm/src/macos/vstate.rs +++ b/src/vmm/src/macos/vstate.rs @@ -17,10 +17,11 @@ use std::time::Duration; use super::super::{FC_EXIT_CODE_GENERIC_ERROR, FC_EXIT_CODE_OK}; use crate::vmm_config::machine_config::CpuFeaturesTemplate; -use arch; use arch::aarch64::gic::GICDevice; +use arch::{self, ShmRegion}; use crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender}; use devices::legacy::{Gic, VcpuList}; +use devices::virtio::VirtioShmRegion; use hvf::{HvfVcpu, HvfVm, VcpuExit}; use utils::eventfd::EventFd; use vm_memory::{ @@ -151,6 +152,17 @@ impl Vm { self.irqchip_handle.as_ref().unwrap() } + pub fn add_shm_region(&mut self, region: ShmRegion) -> Result { + // While on Linux we create a large mmap for the ShmRegion, register it with KVM + // and then add submapping on top of it, on macOS we need to map and register + // each SHM window independently. As a consequence, we don't need to mmap and + // register the whole SHM region. + Ok(VirtioShmRegion { + guest_addr: region.guest_addr, + size: region.size, + }) + } + pub fn add_mapping( &self, reply_sender: Sender,