From e32fad138a13a7f9a2841070ad6b06cd313ddda1 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Sun, 26 Jul 2026 23:03:54 -0700 Subject: [PATCH] refactor(mem): decouple hypervisor and struct Memory Signed-off-by: Changyuan Lyu --- alioth/src/board/board.rs | 49 +++- alioth/src/board/board_amd64/board_amd64.rs | 8 +- alioth/src/board/board_amd64/sev.rs | 27 +- alioth/src/board/board_amd64/tdx.rs | 10 +- alioth/src/board/board_arm64.rs | 2 +- alioth/src/cpu/cpu.rs | 2 +- alioth/src/cpu/cpu_amd64/sev.rs | 18 +- alioth/src/cpu/cpu_amd64/tdx.rs | 3 +- alioth/src/hv/hv.rs | 33 +-- alioth/src/hv/hvf/vcpu/vcpu_test.rs | 9 +- alioth/src/hv/hvf/vm.rs | 84 +++--- .../hv/kvm/vcpu/vcpu_amd64/vcpu_amd64_test.rs | 9 +- alioth/src/hv/kvm/vm/vm.rs | 263 ++++++++---------- alioth/src/hv/kvm/vm/vm_test.rs | 11 +- alioth/src/mem/mem.rs | 73 ++--- alioth/src/pci/config_test.rs | 26 +- 16 files changed, 277 insertions(+), 350 deletions(-) diff --git a/alioth/src/board/board.rs b/alioth/src/board/board.rs index e7bd5e4e..507a58c8 100644 --- a/alioth/src/board/board.rs +++ b/alioth/src/board/board.rs @@ -43,10 +43,10 @@ use crate::device::MmioDev; #[cfg(target_arch = "x86_64")] use crate::device::fw_cfg::FwCfg; use crate::errors::{DebugTrace, trace_error}; -use crate::hv::{CocoSpec, Hypervisor, Vm, VmSpec}; +use crate::hv::{CocoSpec, Hypervisor, MemMapOption, Vm, VmSpec}; use crate::loader::PayloadSpec; use crate::mem::mapped::ArcMemPages; -use crate::mem::{MemBackend, MemRegion, MemRegionType, MemSpec, Memory}; +use crate::mem::{self, LayoutChanged, MemBackend, MemRegion, MemRegionType, MemSpec, Memory}; use crate::pci::bus::PciBus; #[cfg(target_arch = "aarch64")] @@ -162,11 +162,45 @@ impl BoardSpec { } } +#[derive(Debug)] +struct ChangeVmMemory { + vm: Arc, +} + +impl LayoutChanged for ChangeVmMemory +where + V: Vm, +{ + fn ram_added(&self, gpa: u64, pages: &ArcMemPages) -> mem::Result<()> { + let opt = MemMapOption { + read: true, + write: true, + exec: true, + log_dirty: false, + }; + self.vm.map(gpa, pages.size(), pages.addr(), opt)?; + Ok(()) + } + + fn ram_removed(&self, gpa: u64, pages: &ArcMemPages) -> mem::Result<()> { + self.vm.unmap(gpa, pages.size())?; + Ok(()) + } + + fn dev_mem_added(&self, gpa: u64, pages: &ArcMemPages) -> mem::Result<()> { + self.ram_added(gpa, pages) + } + + fn dev_mem_removed(&self, gpa: u64, pages: &ArcMemPages) -> mem::Result<()> { + self.ram_removed(gpa, pages) + } +} + pub struct Board where V: Vm, { - pub vm: V, + pub vm: Arc, pub memory: Memory, pub arch: ArchBoard, pub spec: BoardSpec, @@ -191,13 +225,14 @@ where let vm_spec = VmSpec { coco: spec.coco.clone(), }; - let mut vm = hv.create_vm(&vm_spec)?; - let vm_memory = Arc::new(vm.create_vm_memory()?); + let vm = Arc::new(hv.create_vm(&vm_spec)?); let arch = ArchBoard::new(hv, &vm, &spec)?; + let memory = Memory::new(); + memory.register_change_callback(Box::new(ChangeVmMemory { vm: vm.clone() }))?; let board = Board { vm, - memory: Memory::new(vm_memory.clone()), + memory, arch, spec, payload: RwLock::new(None), @@ -208,7 +243,7 @@ where fw_cfg: Mutex::new(None), }; - board.coco_init(vm_memory)?; + board.coco_init()?; Ok(board) } diff --git a/alioth/src/board/board_amd64/board_amd64.rs b/alioth/src/board/board_amd64/board_amd64.rs index d9111b0f..41a010ca 100644 --- a/alioth/src/board/board_amd64/board_amd64.rs +++ b/alioth/src/board/board_amd64/board_amd64.rs @@ -236,14 +236,14 @@ where Ok(()) } - pub fn coco_init(&self, memory: Arc) -> Result<()> { + pub fn coco_init(&self) -> Result<()> { let Some(coco) = &self.spec.coco else { return Ok(()); }; match coco { - CocoSpec::AmdSev { policy } => self.sev_init(*policy, memory)?, - CocoSpec::AmdSnp { policy } => self.snp_init(*policy, memory)?, - CocoSpec::IntelTdx { attr } => self.tdx_init(*attr, memory)?, + CocoSpec::AmdSev { policy } => self.sev_init(*policy)?, + CocoSpec::AmdSnp { policy } => self.snp_init(*policy)?, + CocoSpec::IntelTdx { attr } => self.tdx_init(*attr)?, } Ok(()) } diff --git a/alioth/src/board/board_amd64/sev.rs b/alioth/src/board/board_amd64/sev.rs index 9bafb759..cc2385be 100644 --- a/alioth/src/board/board_amd64/sev.rs +++ b/alioth/src/board/board_amd64/sev.rs @@ -22,7 +22,7 @@ use crate::arch::cpuid::{ }; use crate::arch::sev::{SevPolicy, SnpPolicy}; use crate::board::{Board, Result, error}; -use crate::hv::{CocoSpec, Vm, VmMemory}; +use crate::hv::{CocoSpec, Vm}; use crate::mem::mapped::ArcMemPages; use crate::mem::{self, LayoutChanged, MarkPrivateMemory}; @@ -108,33 +108,40 @@ impl Board where V: Vm, { - pub(crate) fn sev_init(&self, policy: SevPolicy, memory: Arc) -> Result<()> { + pub(crate) fn sev_init(&self, policy: SevPolicy) -> Result<()> { self.vm.sev_launch_start(policy)?; - let encrypt_pages = Box::new(EncryptPages { memory }); + let encrypt_pages = Box::new(EncryptPages { + vm: self.vm.clone(), + }); self.memory.register_change_callback(encrypt_pages)?; Ok(()) } - pub(crate) fn snp_init(&self, policy: SnpPolicy, memory: Arc) -> Result<()> { + pub(crate) fn snp_init(&self, policy: SnpPolicy) -> Result<()> { self.vm.snp_launch_start(policy)?; let encrypt_pages = Box::new(EncryptPages { - memory: memory.clone(), + vm: self.vm.clone(), }); self.memory.register_change_callback(encrypt_pages)?; - let mark_private_memory = Box::new(MarkPrivateMemory { memory }); + let mark_private_memory = Box::new(MarkPrivateMemory { + vm: self.vm.clone(), + }); self.memory.register_change_callback(mark_private_memory)?; Ok(()) } } #[derive(Debug)] -pub struct EncryptPages { - memory: Arc, +pub struct EncryptPages { + vm: Arc, } -impl LayoutChanged for EncryptPages { +impl LayoutChanged for EncryptPages +where + V: Vm, +{ fn ram_added(&self, _: u64, pages: &ArcMemPages) -> mem::Result<()> { - self.memory.register_encrypted_range(pages.as_slice())?; + self.vm.register_encrypted_range(pages.as_slice())?; Ok(()) } diff --git a/alioth/src/board/board_amd64/tdx.rs b/alioth/src/board/board_amd64/tdx.rs index f1e054be..1b30219e 100644 --- a/alioth/src/board/board_amd64/tdx.rs +++ b/alioth/src/board/board_amd64/tdx.rs @@ -12,20 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::Arc; - use crate::arch::tdx::TdAttr; use crate::board::{Board, Result}; -use crate::hv::{Vm, VmMemory}; +use crate::hv::Vm; use crate::mem::MarkPrivateMemory; impl Board where V: Vm, { - pub(crate) fn tdx_init(&self, attr: TdAttr, memory: Arc) -> Result<()> { + pub(crate) fn tdx_init(&self, attr: TdAttr) -> Result<()> { self.vm.tdx_init_vm(attr, &self.arch.cpuids)?; - let mark_private_memory = Box::new(MarkPrivateMemory { memory }); + let mark_private_memory = Box::new(MarkPrivateMemory { + vm: self.vm.clone(), + }); self.memory.register_change_callback(mark_private_memory)?; Ok(()) } diff --git a/alioth/src/board/board_arm64.rs b/alioth/src/board/board_arm64.rs index 7a39967b..375061df 100644 --- a/alioth/src/board/board_arm64.rs +++ b/alioth/src/board/board_arm64.rs @@ -130,7 +130,7 @@ where Ok(()) } - pub fn coco_init(&self, _: Arc) -> Result<()> { + pub fn coco_init(&self) -> Result<()> { Ok(()) } diff --git a/alioth/src/cpu/cpu.rs b/alioth/src/cpu/cpu.rs index 67e070da..eef652c8 100644 --- a/alioth/src/cpu/cpu.rs +++ b/alioth/src/cpu/cpu.rs @@ -225,7 +225,7 @@ impl VcpuThread { } } VmExit::ConvertMemory { gpa, size, private } => { - memory.mark_private_memory(gpa, size, private)?; + memory.mark_private_memory(&*self.ctx.board.vm, gpa, size, private)?; VmEntry::None } }; diff --git a/alioth/src/cpu/cpu_amd64/sev.rs b/alioth/src/cpu/cpu_amd64/sev.rs index e1d98b7a..ed88cafb 100644 --- a/alioth/src/cpu/cpu_amd64/sev.rs +++ b/alioth/src/cpu/cpu_amd64/sev.rs @@ -74,8 +74,12 @@ where let range_ref = ram.get_slice::(desc.base as u64, desc.len as u64)?; let bytes = unsafe { std::slice::from_raw_parts_mut(range_ref.as_ptr() as _, range_ref.len()) }; - let memory = &self.ctx.board.memory; - memory.mark_private_memory(desc.base as _, desc.len as _, true)?; + self.ctx.board.memory.mark_private_memory( + &*self.ctx.board.vm, + desc.base as _, + desc.len as _, + true, + )?; let vm = &self.ctx.board.vm; let ret = vm.snp_launch_update(bytes, desc.base as _, page_type); if ret.is_err() && desc.type_ == SevDescType::CPUID { @@ -93,7 +97,7 @@ where pub(crate) fn setup_sev(&self, fw: &mut ArcMemPages, policy: SevPolicy) -> Result<()> { let board = &self.ctx.board; - board.memory.register_encrypted_pages(fw)?; + board.vm.register_encrypted_range(fw.as_slice())?; let data = fw.as_slice_mut(); if policy.es() { @@ -104,8 +108,8 @@ where } pub(crate) fn setup_snp(&self, fw: &mut ArcMemPages) -> Result<()> { - let memory = &self.ctx.board.memory; - memory.register_encrypted_pages(fw)?; + let vm = &self.ctx.board.vm; + vm.register_encrypted_range(fw.as_slice())?; let data = fw.as_slice_mut(); self.parse_sev_ap_eip(data)?; @@ -114,8 +118,8 @@ where } let fw_gpa = MEM_64_START - data.len() as u64; - memory.mark_private_memory(fw_gpa, data.len() as _, true)?; - let vm = &self.ctx.board.vm; + let memory = &self.ctx.board.memory; + memory.mark_private_memory(&**vm, fw_gpa, data.len() as _, true)?; vm.snp_launch_update(data, fw_gpa, SnpPageType::NORMAL)?; Ok(()) } diff --git a/alioth/src/cpu/cpu_amd64/tdx.rs b/alioth/src/cpu/cpu_amd64/tdx.rs index 954bfd7c..4bf4f19d 100644 --- a/alioth/src/cpu/cpu_amd64/tdx.rs +++ b/alioth/src/cpu/cpu_amd64/tdx.rs @@ -37,7 +37,8 @@ where let fw_gpa = MEM_64_START - data.len() as u64; let memory = &self.ctx.board.memory; - memory.mark_private_memory(fw_gpa, data.len() as _, true)?; + let vm = &self.ctx.board.vm; + memory.mark_private_memory(&**vm, fw_gpa, data.len() as _, true)?; let mut accepted = Vec::new(); let mut hob_ram = None; diff --git a/alioth/src/hv/hv.rs b/alioth/src/hv/hv.rs index 5253b69c..dc7e6ecf 100644 --- a/alioth/src/hv/hv.rs +++ b/alioth/src/hv/hv.rs @@ -88,8 +88,6 @@ pub enum Error { GuestMsr { error: std::io::Error }, #[snafu(display("Failed to configure memory encryption"))] MemEncrypt { error: std::io::Error }, - #[snafu(display("Cannot create multiple VM memories"))] - MemoryCreated, #[snafu(display("Failed to configure an IrqFd"))] IrqFd { error: std::io::Error }, #[snafu(display("Failed to configure an IoeventFd"))] @@ -245,23 +243,6 @@ pub trait MsiSender: Debug + Send + Sync + 'static { fn create_irqfd(&self) -> Result; } -pub trait VmMemory: Debug + Send + Sync + 'static { - fn mem_map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<(), Error>; - - fn unmap(&self, gpa: u64, size: u64) -> Result<(), Error>; - - fn reset(&self) -> Result<()>; - - fn register_encrypted_range(&self, _range: &[u8]) -> Result<()> { - unimplemented!() - } - fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()> { - unimplemented!() - } - - fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()>; -} - pub trait IoeventFd: Debug + Send + Sync + AsFd + 'static {} pub trait IoeventFdRegistry: Debug + Send + Sync + 'static { @@ -341,9 +322,8 @@ pub struct VmSpec { pub coco: Option, } -pub trait Vm { +pub trait Vm: Debug + Send + Sync + 'static { type Vcpu: Vcpu; - type Memory: VmMemory; type IrqSender: IrqSender + Send + Sync; type MsiSender: MsiSender; type IoeventFdRegistry: IoeventFdRegistry; @@ -353,10 +333,19 @@ pub trait Vm { &self, #[cfg(target_arch = "aarch64")] devid: u32, ) -> Result; - fn create_vm_memory(&mut self) -> Result; fn create_ioeventfd_registry(&self) -> Result; fn stop_vcpu(&self, identity: u64, handle: &JoinHandle) -> Result<(), Error>; + fn map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<(), Error>; + + fn unmap(&self, gpa: u64, size: u64) -> Result<(), Error>; + + fn register_encrypted_range(&self, _range: &[u8]) -> Result<()>; + + fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()>; + + fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()>; + #[cfg(target_arch = "x86_64")] fn sev_launch_start(&self, policy: SevPolicy) -> Result<()>; diff --git a/alioth/src/hv/hvf/vcpu/vcpu_test.rs b/alioth/src/hv/hvf/vcpu/vcpu_test.rs index 8047400d..f3ab3c19 100644 --- a/alioth/src/hv/hvf/vcpu/vcpu_test.rs +++ b/alioth/src/hv/hvf/vcpu/vcpu_test.rs @@ -19,7 +19,7 @@ use libc::{MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ, PROT_WRITE, mmap}; use crate::arch::reg::Reg; use crate::ffi; -use crate::hv::{Hvf, Hypervisor, MemMapOption, Vcpu, Vm, VmEntry, VmExit, VmMemory, VmSpec}; +use crate::hv::{Hvf, Hypervisor, MemMapOption, Vcpu, Vm, VmEntry, VmExit, VmSpec}; #[test] #[cfg_attr(not(feature = "test-hv"), ignore)] @@ -75,8 +75,7 @@ fn test_vcpu_regs() { fn test_vcpu_run() { let hvf = Hvf {}; let spec = VmSpec { coco: None }; - let mut vm = hvf.create_vm(&spec).unwrap(); - let memory = vm.create_vm_memory().unwrap(); + let vm = hvf.create_vm(&spec).unwrap(); let prot = PROT_WRITE | PROT_READ; let flag = MAP_ANONYMOUS | MAP_PRIVATE; @@ -91,9 +90,7 @@ fn test_vcpu_run() { exec: true, ..Default::default() }; - memory - .mem_map(0, 0x4000, user_mem as usize, mmap_option) - .unwrap(); + vm.map(0, 0x4000, user_mem as usize, mmap_option).unwrap(); const CODE: [u8; 20] = [ 0x00, 0x00, 0x8a, 0xd2, // mov x0, #0x5000 diff --git a/alioth/src/hv/hvf/vm.rs b/alioth/src/hv/hvf/vm.rs index 16d9179e..93d9e1af 100644 --- a/alioth/src/hv/hvf/vm.rs +++ b/alioth/src/hv/hvf/vm.rs @@ -28,7 +28,7 @@ use crate::hv::hvf::vcpu::{HvfVcpu, VcpuHandle}; use crate::hv::hvf::{OsObject, check_ret}; use crate::hv::{ GicV2, GicV2m, GicV3, IoeventFd, IoeventFdRegistry, IrqFd, IrqSender, Its, MemMapOption, - MsiSender, Result, Vm, VmMemory, error, + MsiSender, Result, Vm, error, }; use crate::sys::hvf::{ HvMemoryFlag, hv_gic_config_create, hv_gic_config_set_distributor_base, @@ -38,51 +38,6 @@ use crate::sys::hvf::{ hv_vm_unmap, }; -#[derive(Debug)] -pub struct HvfMemory; - -impl VmMemory for HvfMemory { - fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()> { - Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) - } - - fn mem_map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<()> { - if option.log_dirty { - return error::Capability { cap: "log dirty" }.fail(); - } - let mut flags = HvMemoryFlag::empty(); - if option.read { - flags |= HvMemoryFlag::READ; - } - if option.write { - flags |= HvMemoryFlag::WRITE; - } - if option.exec { - flags |= HvMemoryFlag::EXEC; - } - let ret = unsafe { hv_vm_map(hva as *const u8, gpa, size as usize, flags) }; - check_ret(ret).context(error::GuestMap { hva, gpa, size }) - } - - fn register_encrypted_range(&self, _range: &[u8]) -> Result<()> { - Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) - } - - fn unmap(&self, gpa: u64, size: u64) -> Result<()> { - let ret = unsafe { hv_vm_unmap(gpa, size as usize) }; - check_ret(ret).context(error::GuestUnmap { gpa, size })?; - Ok(()) - } - - fn mark_private_memory(&self, _gpa: u64, _size: u64, _private: bool) -> Result<()> { - Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) - } - - fn reset(&self) -> Result<()> { - Ok(()) - } -} - #[derive(Debug)] pub struct HvfIrqSender { spi: u32, @@ -287,7 +242,6 @@ impl Vm for HvfVm { type IoeventFdRegistry = HvfIoeventFdRegistry; type IrqSender = HvfIrqSender; type Its = HvfIts; - type Memory = HvfMemory; type MsiSender = HvfMsiSender; type Vcpu = HvfVcpu; @@ -310,8 +264,40 @@ impl Vm for HvfVm { HvfVcpu::new(self, index, identity) } - fn create_vm_memory(&mut self) -> Result { - Ok(HvfMemory) + fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()> { + Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) + } + + fn map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<()> { + if option.log_dirty { + return error::Capability { cap: "log dirty" }.fail(); + } + let mut flags = HvMemoryFlag::empty(); + if option.read { + flags |= HvMemoryFlag::READ; + } + if option.write { + flags |= HvMemoryFlag::WRITE; + } + if option.exec { + flags |= HvMemoryFlag::EXEC; + } + let ret = unsafe { hv_vm_map(hva as *const u8, gpa, size as usize, flags) }; + check_ret(ret).context(error::GuestMap { hva, gpa, size }) + } + + fn register_encrypted_range(&self, _range: &[u8]) -> Result<()> { + Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) + } + + fn unmap(&self, gpa: u64, size: u64) -> Result<()> { + let ret = unsafe { hv_vm_unmap(gpa, size as usize) }; + check_ret(ret).context(error::GuestUnmap { gpa, size })?; + Ok(()) + } + + fn mark_private_memory(&self, _gpa: u64, _size: u64, _private: bool) -> Result<()> { + Err(ErrorKind::Unsupported.into()).context(error::MemEncrypt) } fn stop_vcpu(&self, identity: u64, _handle: &JoinHandle) -> Result<()> { diff --git a/alioth/src/hv/kvm/vcpu/vcpu_amd64/vcpu_amd64_test.rs b/alioth/src/hv/kvm/vcpu/vcpu_amd64/vcpu_amd64_test.rs index 6a373b5d..0c4f2ee3 100644 --- a/alioth/src/hv/kvm/vcpu/vcpu_amd64/vcpu_amd64_test.rs +++ b/alioth/src/hv/kvm/vcpu/vcpu_amd64/vcpu_amd64_test.rs @@ -24,7 +24,7 @@ use crate::arch::reg::{Cr0, Cr4, Reg, SegAccess}; use crate::ffi; use crate::hv::{ DtReg, DtRegVal, Hypervisor, Kvm, MemMapOption, SReg, SegReg, SegRegVal, Vcpu, Vm, VmEntry, - VmExit, VmMemory, + VmExit, }; #[test] @@ -181,8 +181,7 @@ fn test_kvm_run() { let kvm = Kvm::new(KvmSpec::default()).unwrap(); let spec = VmSpec { coco: None }; - let mut vm = kvm.create_vm(&spec).unwrap(); - let memory = vm.create_vm_memory().unwrap(); + let vm = kvm.create_vm(&spec).unwrap(); let prot = PROT_WRITE | PROT_EXEC | PROT_READ; let flag = MAP_ANONYMOUS | MAP_SHARED; @@ -197,9 +196,7 @@ fn test_kvm_run() { exec: true, ..Default::default() }; - memory - .mem_map(0, 0x5000, user_mem as usize, mmap_option) - .unwrap(); + vm.map(0, 0x5000, user_mem as usize, mmap_option).unwrap(); // layout // 0x1000 - 0x1f00 code diff --git a/alioth/src/hv/kvm/vm/vm.rs b/alioth/src/hv/kvm/vm/vm.rs index f2c46a3d..89c60c1c 100644 --- a/alioth/src/hv/kvm/vm/vm.rs +++ b/alioth/src/hv/kvm/vm/vm.rs @@ -46,7 +46,7 @@ use crate::hv::kvm::vcpu::KvmVcpu; use crate::hv::kvm::{KvmError, check_extension, kvm_error}; use crate::hv::{ Error, IoeventFd, IoeventFdRegistry, IrqFd, IrqSender, Kvm, MemMapOption, MsiSender, Result, - Vm, VmMemory, VmSpec, error, + Vm, VmSpec, error, }; #[cfg(target_arch = "x86_64")] use crate::sys::kvm::KVM_IRQCHIP_IOAPIC; @@ -152,139 +152,6 @@ impl Display for VmInner { } } -type MemSlots = (u32, HashMap<(u64, u64), u32>); - -#[derive(Debug)] -pub struct KvmMemory { - slots: Mutex, - vm: Arc, -} - -impl KvmMemory { - pub fn new(vm: &KvmVm) -> Self { - KvmMemory { - slots: Mutex::new((0, HashMap::new())), - vm: vm.vm.clone(), - } - } - - fn unmap(&self, slot: u32, gpa: u64, size: u64) -> Result<()> { - let flags = KvmMemFlag::empty(); - let region = KvmUserspaceMemoryRegion { - slot, - guest_phys_addr: gpa, - memory_size: 0, - userspace_addr: 0, - flags, - }; - unsafe { kvm_set_user_memory_region(&self.vm.fd, ®ion) } - .context(error::GuestUnmap { gpa, size })?; - log::trace!( - "{}: slot-{slot}: unmapped: {gpa:#018x}, size={size:#x}", - self.vm - ); - Ok(()) - } -} - -impl VmMemory for KvmMemory { - fn mem_map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<(), Error> { - let mut flags = KvmMemFlag::empty(); - if !option.read || !option.exec { - return kvm_error::MmapOption { option }.fail()?; - } - if !option.write { - flags |= KvmMemFlag::READONLY; - } - if option.log_dirty { - flags |= KvmMemFlag::LOG_DIRTY_PAGES; - } - let (slot_id, slots) = &mut *self.slots.lock(); - if let Some(memfd) = &self.vm.memfd { - flags |= KvmMemFlag::GUEST_MEMFD; - let region = KvmUserspaceMemoryRegion2 { - slot: *slot_id, - guest_phys_addr: gpa as _, - memory_size: size as _, - userspace_addr: hva as _, - flags, - guest_memfd: memfd.as_raw_fd() as _, - guest_memfd_offset: gpa, - ..Default::default() - }; - unsafe { kvm_set_user_memory_region2(&self.vm.fd, ®ion) } - } else { - let region = KvmUserspaceMemoryRegion { - slot: *slot_id, - guest_phys_addr: gpa as _, - memory_size: size as _, - userspace_addr: hva as _, - flags, - }; - unsafe { kvm_set_user_memory_region(&self.vm.fd, ®ion) } - } - .context(error::GuestMap { hva, gpa, size })?; - slots.insert((gpa, size), *slot_id); - log::trace!( - "{}: slot-{slot_id}: mapped: {gpa:#018x} -> {hva:#018x}, size = {size:#x}", - self.vm - ); - *slot_id += 1; - Ok(()) - } - - fn unmap(&self, gpa: u64, size: u64) -> Result<(), Error> { - let (_, slots) = &mut *self.slots.lock(); - let Some(slot) = slots.remove(&(gpa, size)) else { - return Err(ErrorKind::NotFound.into()).context(error::GuestUnmap { gpa, size }); - }; - self.unmap(slot, gpa, size) - } - - fn register_encrypted_range(&self, range: &[u8]) -> Result<()> { - let region = KvmEncRegion { - addr: range.as_ptr() as u64, - size: range.len() as u64, - }; - unsafe { kvm_memory_encrypt_reg_region(&self.vm.fd, ®ion) } - .context(error::MemEncrypt)?; - Ok(()) - } - - fn deregister_encrypted_range(&self, range: &[u8]) -> Result<()> { - let region = KvmEncRegion { - addr: range.as_ptr() as u64, - size: range.len() as u64, - }; - unsafe { kvm_memory_encrypt_unreg_region(&self.vm.fd, ®ion) } - .context(error::MemEncrypt)?; - Ok(()) - } - - fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()> { - let attr = KvmMemoryAttributes { - address: gpa, - size, - attributes: if private { - KvmMemoryAttribute::PRIVATE - } else { - KvmMemoryAttribute::empty() - }, - flags: 0, - }; - unsafe { kvm_set_memory_attributes(&self.vm.fd, &attr) }.context(error::MemEncrypt)?; - Ok(()) - } - - fn reset(&self) -> Result<()> { - let (slot_id, slots) = &mut *self.slots.lock(); - for ((gpa, size), slot) in slots.drain() { - self.unmap(slot, gpa, size)?; - } - *slot_id = 0; - Ok(()) - } -} #[derive(Debug)] pub struct KvmIrqSender { pin: u8, @@ -594,9 +461,16 @@ impl IoeventFdRegistry for KvmIoeventFdRegistry { } } +#[derive(Debug, Default)] +struct MemSlots { + next_id: u32, + mapped: HashMap<(u64, u64), u32>, +} + +#[derive(Debug)] pub struct KvmVm { pub vm: Arc, - memory_created: bool, + mem_slots: Mutex, } impl KvmVm { @@ -619,7 +493,7 @@ impl KvmVm { pin_map: AtomicU32::new(0), arch, }), - memory_created: false, + mem_slots: Mutex::new(MemSlots::default()), }; kvm_vm.init(spec)?; Ok(kvm_vm) @@ -637,7 +511,6 @@ impl Vm for KvmVm { type IrqSender = KvmIrqSender; #[cfg(target_arch = "aarch64")] type Its = aarch64::KvmIts; - type Memory = KvmMemory; type MsiSender = KvmMsiSender; type Vcpu = KvmVcpu; @@ -651,16 +524,6 @@ impl Vm for KvmVm { Ok(()) } - fn create_vm_memory(&mut self) -> Result { - if self.memory_created { - error::MemoryCreated.fail() - } else { - let kvm_memory = KvmMemory::new(self); - self.memory_created = true; - Ok(kvm_memory) - } - } - fn create_irq_sender(&self, pin: u8) -> Result { let pin_flag = 1 << pin; if self.vm.pin_map.fetch_or(pin_flag, Ordering::AcqRel) & pin_flag == pin_flag { @@ -701,6 +564,112 @@ impl Vm for KvmVm { }) } + fn map(&self, gpa: u64, size: u64, hva: usize, option: MemMapOption) -> Result<(), Error> { + let mut flags = KvmMemFlag::empty(); + if !option.read || !option.exec { + return kvm_error::MmapOption { option }.fail()?; + } + if !option.write { + flags |= KvmMemFlag::READONLY; + } + if option.log_dirty { + flags |= KvmMemFlag::LOG_DIRTY_PAGES; + } + let mut slots = self.mem_slots.lock(); + let slot_id = slots.next_id; + slots.next_id += 1; + if let Some(memfd) = &self.vm.memfd { + flags |= KvmMemFlag::GUEST_MEMFD; + let region = KvmUserspaceMemoryRegion2 { + slot: slot_id, + guest_phys_addr: gpa as _, + memory_size: size as _, + userspace_addr: hva as _, + flags, + guest_memfd: memfd.as_raw_fd() as _, + guest_memfd_offset: gpa, + ..Default::default() + }; + unsafe { kvm_set_user_memory_region2(&self.vm.fd, ®ion) } + } else { + let region = KvmUserspaceMemoryRegion { + slot: slot_id, + guest_phys_addr: gpa as _, + memory_size: size as _, + userspace_addr: hva as _, + flags, + }; + unsafe { kvm_set_user_memory_region(&self.vm.fd, ®ion) } + } + .context(error::GuestMap { hva, gpa, size })?; + slots.mapped.insert((gpa, size), slot_id); + log::trace!( + "{}: slot-{slot_id}: mapped: {gpa:#018x} -> {hva:#018x}, size = {size:#x}", + self.vm + ); + Ok(()) + } + + fn unmap(&self, gpa: u64, size: u64) -> Result<(), Error> { + let mem_slots = &mut *self.mem_slots.lock(); + let Some(slot) = mem_slots.mapped.remove(&(gpa, size)) else { + return Err(ErrorKind::NotFound.into()).context(error::GuestUnmap { gpa, size }); + }; + let flags = KvmMemFlag::empty(); + let region = KvmUserspaceMemoryRegion { + slot, + guest_phys_addr: gpa, + memory_size: 0, + userspace_addr: 0, + flags, + }; + unsafe { kvm_set_user_memory_region(&self.vm.fd, ®ion) } + .context(error::GuestUnmap { gpa, size })?; + log::trace!( + "{}: slot-{slot}: unmapped: {gpa:#018x}, size={size:#x}", + self.vm + ); + if mem_slots.mapped.is_empty() { + mem_slots.next_id = 0; + } + Ok(()) + } + + fn register_encrypted_range(&self, range: &[u8]) -> Result<()> { + let region = KvmEncRegion { + addr: range.as_ptr() as u64, + size: range.len() as u64, + }; + unsafe { kvm_memory_encrypt_reg_region(&self.vm.fd, ®ion) } + .context(error::MemEncrypt)?; + Ok(()) + } + + fn deregister_encrypted_range(&self, range: &[u8]) -> Result<()> { + let region = KvmEncRegion { + addr: range.as_ptr() as u64, + size: range.len() as u64, + }; + unsafe { kvm_memory_encrypt_unreg_region(&self.vm.fd, ®ion) } + .context(error::MemEncrypt)?; + Ok(()) + } + + fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()> { + let attr = KvmMemoryAttributes { + address: gpa, + size, + attributes: if private { + KvmMemoryAttribute::PRIVATE + } else { + KvmMemoryAttribute::empty() + }, + flags: 0, + }; + unsafe { kvm_set_memory_attributes(&self.vm.fd, &attr) }.context(error::MemEncrypt)?; + Ok(()) + } + #[cfg(target_arch = "x86_64")] fn sev_launch_start(&self, policy: SevPolicy) -> Result<(), Error> { KvmVm::sev_launch_start(self, policy) diff --git a/alioth/src/hv/kvm/vm/vm_test.rs b/alioth/src/hv/kvm/vm/vm_test.rs index 7c077a45..4b4f92ee 100644 --- a/alioth/src/hv/kvm/vm/vm_test.rs +++ b/alioth/src/hv/kvm/vm/vm_test.rs @@ -27,8 +27,7 @@ use crate::hv::{Hypervisor, Kvm, MemMapOption, VmSpec}; fn test_mem_map() { let kvm = Kvm::new(KvmSpec::default()).unwrap(); let spec = VmSpec { coco: None }; - let mut vm = kvm.create_vm(&spec).unwrap(); - let vm_memory = vm.create_vm_memory().unwrap(); + let vm = kvm.create_vm(&spec).unwrap(); let prot = PROT_WRITE | PROT_READ | PROT_EXEC; let flag = MAP_ANONYMOUS | MAP_PRIVATE; @@ -44,7 +43,7 @@ fn test_mem_map() { log_dirty: true, }; assert_matches!( - vm_memory.mem_map(0x0, 0x1000, user_mem as usize, option_no_write), + vm.map(0x0, 0x1000, user_mem as usize, option_no_write), Err(Error::KvmErr { .. }) ); let option_no_exec = MemMapOption { @@ -54,7 +53,7 @@ fn test_mem_map() { log_dirty: true, }; assert_matches!( - vm_memory.mem_map(0x0, 0x1000, user_mem as usize, option_no_exec), + vm.map(0x0, 0x1000, user_mem as usize, option_no_exec), Err(Error::KvmErr { .. }) ); let option = MemMapOption { @@ -63,7 +62,5 @@ fn test_mem_map() { exec: true, log_dirty: true, }; - vm_memory - .mem_map(0x0, 0x1000, user_mem as usize, option) - .unwrap(); + vm.map(0x0, 0x1000, user_mem as usize, option).unwrap(); } diff --git a/alioth/src/mem/mem.rs b/alioth/src/mem/mem.rs index 5ff5a13e..fdfe5919 100644 --- a/alioth/src/mem/mem.rs +++ b/alioth/src/mem/mem.rs @@ -28,7 +28,7 @@ use snafu::Snafu; #[cfg(target_arch = "aarch64")] use crate::arch::layout::IO_START; use crate::errors::{DebugTrace, trace_error}; -use crate::hv::{MemMapOption, VmEntry, VmMemory}; +use crate::hv::{Vm, VmEntry}; use self::addressable::{Addressable, SlotBackend}; use self::emulated::{Action, Mmio, MmioBus}; @@ -260,13 +260,12 @@ struct LayoutCallbacks { } // lock order: region -> callbacks -> bus -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Memory { regions: Mutex>>, callbacks: Mutex, ram_bus: Arc, mmio_bus: RwLock, - vm_memory: Arc, #[cfg(target_arch = "x86_64")] io_bus: RwLock, @@ -274,17 +273,8 @@ pub struct Memory { } impl Memory { - pub fn new(vm_memory: Arc) -> Self { - Memory { - regions: Mutex::new(Addressable::new()), - callbacks: Mutex::new(LayoutCallbacks::default()), - ram_bus: Arc::new(RamBus::new()), - mmio_bus: RwLock::new(MmioBus::new()), - vm_memory, - #[cfg(target_arch = "x86_64")] - io_bus: RwLock::new(MmioBus::new()), - io_regions: Mutex::new(Addressable::new()), - } + pub fn new() -> Self { + Memory::default() } pub fn register_change_callback(&self, callback: Box) -> Result<()> { @@ -316,7 +306,6 @@ impl Memory { pub fn reset(&self) -> Result<()> { self.clear()?; - self.vm_memory.reset()?; Ok(()) } @@ -324,23 +313,6 @@ impl Memory { self.ram_bus.clone() } - fn map_to_vm(&self, gpa: u64, user_mem: &ArcMemPages) -> Result<(), Error> { - let mem_options = MemMapOption { - read: true, - write: true, - exec: true, - log_dirty: false, - }; - self.vm_memory - .mem_map(gpa, user_mem.size(), user_mem.addr(), mem_options)?; - Ok(()) - } - - fn unmap_from_vm(&self, gpa: u64, user_mem: &ArcMemPages) -> Result<(), Error> { - self.vm_memory.unmap(gpa, user_mem.size())?; - Ok(()) - } - pub fn add_mmio_dev(&self, addr: u64, dev: Arc) -> Result<()> { let region = MemRegion::with_emulated(dev, MemRegionType::Hidden); self.add_region(addr, Arc::new(region)) @@ -357,7 +329,6 @@ impl Memory { let gpa = addr + offset; if let MemRange::Ram(r) | MemRange::DevMem(r) = range { self.ram_bus.add(gpa, r.clone())?; - self.map_to_vm(gpa, r)?; ram_updated = true; } match range { @@ -398,9 +369,8 @@ impl Memory { let mut ram_updated = false; for range in ®ion.ranges { let gpa = addr + offset; - if let MemRange::Ram(r) | MemRange::DevMem(r) = range { + if let MemRange::Ram(_) | MemRange::DevMem(_) = range { self.ram_bus.remove(gpa)?; - self.unmap_from_vm(gpa, r)?; ram_updated = true; } match range { @@ -528,19 +498,13 @@ impl Memory { Ok(io_region) } - pub fn register_encrypted_pages(&self, pages: &ArcMemPages) -> Result<()> { - self.vm_memory.register_encrypted_range(pages.as_slice())?; - Ok(()) - } - - pub fn deregister_encrypted_pages(&self, pages: &ArcMemPages) -> Result<()> { - self.vm_memory - .deregister_encrypted_range(pages.as_slice())?; - Ok(()) - } - - pub fn mark_private_memory(&self, gpa: u64, size: u64, private: bool) -> Result<()> { - let vm_memory = &self.vm_memory; + pub fn mark_private_memory( + &self, + vm: &V, + gpa: u64, + size: u64, + private: bool, + ) -> Result<()> { let regions = self.regions.lock(); let end = gpa + size; let mut start = gpa; @@ -561,7 +525,7 @@ impl Memory { if gpa_start >= gpa_end { break 'out; } - vm_memory.mark_private_memory(gpa_start, gpa_end - gpa_start, private)?; + vm.mark_private_memory(gpa_start, gpa_end - gpa_start, private)?; start = gpa_end; } if next_start >= end { @@ -621,13 +585,16 @@ impl Memory { } #[derive(Debug)] -pub struct MarkPrivateMemory { - pub memory: Arc, +pub struct MarkPrivateMemory { + pub vm: Arc, } -impl LayoutChanged for MarkPrivateMemory { +impl LayoutChanged for MarkPrivateMemory +where + V: Vm, +{ fn ram_added(&self, gpa: u64, pages: &ArcMemPages) -> Result<()> { - self.memory.mark_private_memory(gpa, pages.size(), true)?; + self.vm.mark_private_memory(gpa, pages.size(), true)?; Ok(()) } diff --git a/alioth/src/pci/config_test.rs b/alioth/src/pci/config_test.rs index 4be79929..b2244138 100644 --- a/alioth/src/pci/config_test.rs +++ b/alioth/src/pci/config_test.rs @@ -18,7 +18,6 @@ use std::sync::Arc; use assert_matches::assert_matches; use rstest::rstest; -use crate::hv::{self, MemMapOption, VmMemory}; use crate::mem::emulated::{Action, Mmio}; use crate::mem::{self, IoRegion, MemRegion, MemRegionEntry, MemRegionType, Memory}; use crate::pci::cap::{MsixCap, MsixCapMmio, NullCap, PciCap, PciCapHdr, PciCapId, PciCapList}; @@ -268,30 +267,9 @@ fn test_emulated_config() { assert_matches!(config.read(0x42, 2), Ok(0)); } -#[derive(Debug)] -struct FakeVmMemory; - -impl VmMemory for FakeVmMemory { - fn mem_map(&self, _gpa: u64, _size: u64, _hva: usize, _option: MemMapOption) -> hv::Result<()> { - unreachable!() - } - - fn unmap(&self, _gpa: u64, _size: u64) -> hv::Result<()> { - unreachable!() - } - - fn mark_private_memory(&self, _gpa: u64, _size: u64, _private: bool) -> hv::Result<()> { - unreachable!() - } - - fn reset(&self) -> hv::Result<()> { - unreachable!() - } -} - #[test] fn test_mem_bar_layout_change() { - let memory = Memory::new(Arc::new(FakeVmMemory)); + let memory = Memory::new(); let header = fixture_emulated_header(); let callback = assert_matches!( @@ -351,7 +329,7 @@ fn test_mem_bar_layout_change() { #[test] fn test_io_bar_layout_change() { - let memory = Memory::new(Arc::new(FakeVmMemory)); + let memory = Memory::new(); let header = fixture_emulated_header(); let callback = assert_matches!(