From 376f8ce99eef143cab1b84e89dfa8d28dc29dd98 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Tue, 10 Sep 2024 01:45:48 +0700 Subject: [PATCH] Makes RAM belong to hypervisor (#976) --- src/core.h | 36 ------ src/core/src/vmm/aarch64.rs | 2 +- src/core/src/vmm/hv/linux/mod.rs | 139 ++++++++++++----------- src/core/src/vmm/hv/macos/mod.rs | 39 ++++--- src/core/src/vmm/hv/mod.rs | 32 ++---- src/core/src/vmm/hv/windows/mod.rs | 46 ++++---- src/core/src/vmm/hw/console/context.rs | 19 ++-- src/core/src/vmm/hw/console/mod.rs | 10 +- src/core/src/vmm/hw/mod.rs | 31 ++--- src/core/src/vmm/mod.rs | 59 +++++----- src/core/src/vmm/{hw => }/ram/builder.rs | 81 ++++--------- src/core/src/vmm/{hw => }/ram/mod.rs | 48 ++++++++ src/core/src/vmm/x86_64.rs | 2 +- 13 files changed, 260 insertions(+), 284 deletions(-) rename src/core/src/vmm/{hw => }/ram/builder.rs (91%) rename src/core/src/vmm/{hw => }/ram/mod.rs (79%) diff --git a/src/core.h b/src/core.h index b659d5fd3..39b0fbade 100644 --- a/src/core.h +++ b/src/core.h @@ -170,42 +170,6 @@ struct Vmm *vmm_run(const char *kernel, struct RustError *vmm_draw(struct Vmm *vmm); -#if defined(__linux__) -extern int kvm_check_version(int kvm, bool *compat); -#endif - -#if defined(__linux__) -extern int kvm_max_vcpus(int kvm, size_t *max); -#endif - -#if defined(__linux__) -extern int kvm_create_vm(int kvm, int *fd); -#endif - -#if defined(__linux__) -extern int kvm_get_vcpu_mmap_size(int kvm); -#endif - -#if defined(__linux__) -extern int kvm_set_user_memory_region(int vm, - uint32_t slot, - uint64_t addr, - uint64_t len, - void *mem); -#endif - -#if defined(__linux__) -extern int kvm_create_vcpu(int vm, uint32_t id, int *fd); -#endif - -#if defined(__linux__) -extern int kvm_run(int vcpu); -#endif - -#if defined(__linux__) -extern int kvm_translate(int vcpu, kvm_translation *arg); -#endif - #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/src/core/src/vmm/aarch64.rs b/src/core/src/vmm/aarch64.rs index 02ebca9ae..166adf984 100644 --- a/src/core/src/vmm/aarch64.rs +++ b/src/core/src/vmm/aarch64.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::hv::{Cpu, CpuFeats, CpuStates, Pstate}; -use super::hw::RamMap; +use super::ram::RamMap; use super::MainCpuError; pub fn setup_main_cpu( diff --git a/src/core/src/vmm/hv/linux/mod.rs b/src/core/src/vmm/hv/linux/mod.rs index 2daf9374c..80a4e5869 100644 --- a/src/core/src/vmm/hv/linux/mod.rs +++ b/src/core/src/vmm/hv/linux/mod.rs @@ -5,7 +5,7 @@ use self::ffi::{ kvm_set_user_memory_region, }; use super::{CpuFeats, Hypervisor}; -use crate::vmm::hw::Ram; +use crate::vmm::ram::Ram; use crate::vmm::VmmError; use libc::{mmap, open, MAP_FAILED, MAP_PRIVATE, O_RDWR, PROT_READ, PROT_WRITE}; use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; @@ -20,87 +20,84 @@ mod ffi; mod regs; mod run; -/// Implementation of [`Hypervisor`] using KVM. -/// -/// Fields in this struct need to drop in a correct order (e.g. vm must be dropped before ram). -pub struct Kvm { - vcpu_mmap_size: usize, - vm: OwnedFd, - #[allow(dead_code)] // ram are needed by vm. - ram: Arc, - #[allow(dead_code)] // kvm are needed by vm. - kvm: OwnedFd, -} - -impl Kvm { - pub fn new(cpu: usize, ram: Arc) -> Result { - use std::io::Error; +pub fn new(cpu: usize, ram: Ram) -> Result { + use std::io::Error; - // Open KVM device. - let kvm = unsafe { open(b"/dev/kvm\0".as_ptr().cast(), O_RDWR) }; + // Open KVM device. + let kvm = unsafe { open(b"/dev/kvm\0".as_ptr().cast(), O_RDWR) }; - if kvm < 0 { - return Err(VmmError::OpenKvmFailed(Error::last_os_error())); - } + if kvm < 0 { + return Err(VmmError::OpenKvmFailed(Error::last_os_error())); + } - // Check KVM version. - let kvm = unsafe { OwnedFd::from_raw_fd(kvm) }; - let mut compat = false; + // Check KVM version. + let kvm = unsafe { OwnedFd::from_raw_fd(kvm) }; + let mut compat = false; - match unsafe { kvm_check_version(kvm.as_raw_fd(), &mut compat) } { - 0 if !compat => { - return Err(VmmError::KvmVersionMismatched); - } - 0 => {} - v => return Err(VmmError::GetKvmVersionFailed(Error::from_raw_os_error(v))), + match unsafe { kvm_check_version(kvm.as_raw_fd(), &mut compat) } { + 0 if !compat => { + return Err(VmmError::KvmVersionMismatched); } + 0 => {} + v => return Err(VmmError::GetKvmVersionFailed(Error::from_raw_os_error(v))), + } - // Check max CPU. - let mut max = 0; + // Check max CPU. + let mut max = 0; - match unsafe { kvm_max_vcpus(kvm.as_raw_fd(), &mut max) } { - 0 => {} - v => { - return Err(VmmError::GetMaxCpuFailed(Error::from_raw_os_error(v))); - } + match unsafe { kvm_max_vcpus(kvm.as_raw_fd(), &mut max) } { + 0 => {} + v => { + return Err(VmmError::GetMaxCpuFailed(Error::from_raw_os_error(v))); } + } - if max < cpu { - return Err(VmmError::MaxCpuTooLow); - } + if max < cpu { + return Err(VmmError::MaxCpuTooLow); + } - // Get size of CPU context. - let vcpu_mmap_size = match unsafe { kvm_get_vcpu_mmap_size(kvm.as_raw_fd()) } { - size @ 0.. => size as usize, - _ => return Err(VmmError::GetMmapSizeFailed(Error::last_os_error())), - }; + // Get size of CPU context. + let vcpu_mmap_size = match unsafe { kvm_get_vcpu_mmap_size(kvm.as_raw_fd()) } { + size @ 0.. => size as usize, + _ => return Err(VmmError::GetMmapSizeFailed(Error::last_os_error())), + }; - // Create a VM. - let mut vm = -1; + // Create a VM. + let mut vm = -1; - match unsafe { kvm_create_vm(kvm.as_raw_fd(), &mut vm) } { - 0 => {} - v => return Err(VmmError::CreateVmFailed(Error::from_raw_os_error(v))), - } - - // Set RAM. - let vm = unsafe { OwnedFd::from_raw_fd(vm) }; - let slot = 0; - let len = ram.len().try_into().unwrap(); - let mem = ram.host_addr().cast_mut().cast(); + match unsafe { kvm_create_vm(kvm.as_raw_fd(), &mut vm) } { + 0 => {} + v => return Err(VmmError::CreateVmFailed(Error::from_raw_os_error(v))), + } - match unsafe { kvm_set_user_memory_region(vm.as_raw_fd(), slot, 0, len, mem) } { - 0 => {} - v => return Err(VmmError::MapRamFailed(Error::from_raw_os_error(v))), - } + // Set RAM. + let vm = unsafe { OwnedFd::from_raw_fd(vm) }; + let slot = 0; + let len = ram.len().try_into().unwrap(); + let mem = ram.host_addr().cast_mut().cast(); - Ok(Self { - vcpu_mmap_size, - vm, - ram, - kvm, - }) + match unsafe { kvm_set_user_memory_region(vm.as_raw_fd(), slot, 0, len, mem) } { + 0 => {} + v => return Err(VmmError::MapRamFailed(Error::from_raw_os_error(v))), } + + Ok(Kvm { + vcpu_mmap_size, + vm, + ram, + kvm, + }) +} + +/// Implementation of [`Hypervisor`] using KVM. +/// +/// Fields in this struct need to drop in a correct order (e.g. vm must be dropped before ram). +struct Kvm { + vcpu_mmap_size: usize, + vm: OwnedFd, + ram: Ram, + #[allow(dead_code)] // kvm are needed by vm. + kvm: OwnedFd, } impl Hypervisor for Kvm { @@ -111,6 +108,14 @@ impl Hypervisor for Kvm { Ok(CpuFeats {}) } + fn ram(&self) -> &Ram { + &self.ram + } + + fn ram_mut(&mut self) -> &mut Ram { + &mut self.ram + } + fn create_cpu(&self, id: usize) -> Result, Self::CpuErr> { use std::io::Error; diff --git a/src/core/src/vmm/hv/macos/mod.rs b/src/core/src/vmm/hv/macos/mod.rs index 594198f67..cabcde695 100644 --- a/src/core/src/vmm/hv/macos/mod.rs +++ b/src/core/src/vmm/hv/macos/mod.rs @@ -2,12 +2,11 @@ use self::cpu::HfCpu; use self::vm::Vm; use super::{CpuFeats, Hypervisor}; -use crate::vmm::hw::Ram; +use crate::vmm::ram::Ram; use crate::vmm::VmmError; use hv_sys::hv_vcpu_create; use std::ffi::c_int; use std::num::NonZero; -use std::sync::Arc; use thiserror::Error; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] @@ -16,25 +15,23 @@ mod arch; mod cpu; mod vm; +pub fn new(_: usize, ram: Ram) -> Result { + // Create a VM. + let vm = Vm::new().map_err(VmmError::CreateVmFailed)?; + + // Map memory. + vm.vm_map(ram.host_addr().cast_mut().cast(), 0, ram.len()) + .map_err(VmmError::MapRamFailed)?; + + Ok(Hf { vm, ram }) +} + /// Implementation of [`Hypervisor`] using Hypervisor Framework. /// /// Fields in this struct need to drop in a correct order. -pub struct Hf { +struct Hf { vm: Vm, - ram: Arc, -} - -impl Hf { - pub fn new(_: usize, ram: Arc) -> Result { - // Create a VM. - let vm = Vm::new().map_err(VmmError::CreateVmFailed)?; - - // Map memory. - vm.vm_map(ram.host_addr().cast_mut().cast(), 0, ram.len()) - .map_err(VmmError::MapRamFailed)?; - - Ok(Self { vm, ram }) - } + ram: Ram, } impl Hypervisor for Hf { @@ -120,6 +117,14 @@ impl Hypervisor for Hf { Ok(CpuFeats {}) } + fn ram(&self) -> &Ram { + &self.ram + } + + fn ram_mut(&mut self) -> &mut Ram { + &mut self.ram + } + fn create_cpu(&self, _: usize) -> Result, Self::CpuErr> { let mut instance = 0; diff --git a/src/core/src/vmm/hv/mod.rs b/src/core/src/vmm/hv/mod.rs index aca69d395..935a52627 100644 --- a/src/core/src/vmm/hv/mod.rs +++ b/src/core/src/vmm/hv/mod.rs @@ -1,35 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use super::hw::Ram; -use super::VmmError; +use super::ram::Ram; use std::error::Error; -use std::sync::Arc; pub use self::arch::*; +pub use self::os::new; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] mod arch; -#[cfg(target_os = "linux")] -mod linux; -#[cfg(target_os = "macos")] -mod macos; -#[cfg(target_os = "windows")] -mod windows; - -#[cfg(target_os = "linux")] -pub fn new(cpu: usize, ram: Arc) -> Result { - self::linux::Kvm::new(cpu, ram) -} - -#[cfg(target_os = "windows")] -pub fn new(cpu: usize, ram: Arc) -> Result { - self::windows::Whp::new(cpu, ram) -} - -#[cfg(target_os = "macos")] -pub fn new(cpu: usize, ram: Arc) -> Result { - self::macos::Hf::new(cpu, ram) -} +#[cfg_attr(target_os = "linux", path = "linux/mod.rs")] +#[cfg_attr(target_os = "macos", path = "macos/mod.rs")] +#[cfg_attr(target_os = "windows", path = "windows/mod.rs")] +mod os; /// Underlying hypervisor (e.g. KVM on Linux). pub trait Hypervisor: Send + Sync { @@ -39,6 +21,8 @@ pub trait Hypervisor: Send + Sync { type CpuErr: Error + Send + 'static; fn cpu_features(&mut self) -> Result; + fn ram(&self) -> &Ram; + fn ram_mut(&mut self) -> &mut Ram; /// This method must be called by a thread that is going to drive the returned CPU. fn create_cpu(&self, id: usize) -> Result, Self::CpuErr>; diff --git a/src/core/src/vmm/hv/windows/mod.rs b/src/core/src/vmm/hv/windows/mod.rs index 3e64d168f..315a2b4b9 100644 --- a/src/core/src/vmm/hv/windows/mod.rs +++ b/src/core/src/vmm/hv/windows/mod.rs @@ -1,7 +1,7 @@ use self::cpu::WhpCpu; use self::partition::Partition; use super::{CpuFeats, Hypervisor}; -use crate::vmm::hw::Ram; +use crate::vmm::ram::Ram; use crate::vmm::VmmError; use std::sync::Arc; use thiserror::Error; @@ -10,29 +10,27 @@ use windows_sys::core::HRESULT; mod cpu; mod partition; -/// Implementation of [`Hypervisor`] using Windows Hypervisor Platform. -/// -/// Fields in this struct need to drop in a correct order. -pub struct Whp { - part: Partition, - ram: Arc, -} +pub fn new(cpu: usize, ram: Ram) -> Result { + // Setup a partition. + let mut part = Partition::new().map_err(VmmError::CreatePartitionFailed)?; -impl Whp { - pub fn new(cpu: usize, ram: Arc) -> Result { - // Setup a partition. - let mut part = Partition::new().map_err(VmmError::CreatePartitionFailed)?; + part.set_processor_count(cpu) + .map_err(VmmError::SetCpuCountFailed)?; + part.setup().map_err(VmmError::SetupPartitionFailed)?; - part.set_processor_count(cpu) - .map_err(VmmError::SetCpuCountFailed)?; - part.setup().map_err(VmmError::SetupPartitionFailed)?; + // Map memory. + part.map_gpa(ram.host_addr().cast(), 0, ram.len().try_into().unwrap()) + .map_err(VmmError::MapRamFailed)?; - // Map memory. - part.map_gpa(ram.host_addr().cast(), 0, ram.len().try_into().unwrap()) - .map_err(VmmError::MapRamFailed)?; + Ok(Whp { part, ram }) +} - Ok(Self { part, ram }) - } +/// Implementation of [`Hypervisor`] using Windows Hypervisor Platform. +/// +/// Fields in this struct need to drop in a correct order. +struct Whp { + part: Partition, + ram: Ram, } impl Hypervisor for Whp { @@ -43,6 +41,14 @@ impl Hypervisor for Whp { Ok(CpuFeats {}) } + fn ram(&self) -> &Ram { + &self.ram + } + + fn ram_mut(&mut self) -> &mut Ram { + &mut self.ram + } + fn create_cpu(&self, id: usize) -> Result, Self::CpuErr> { let id = id.try_into().unwrap(); diff --git a/src/core/src/vmm/hw/console/context.rs b/src/core/src/vmm/hw/console/context.rs index e32122471..027559156 100644 --- a/src/core/src/vmm/hw/console/context.rs +++ b/src/core/src/vmm/hw/console/context.rs @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 use super::Console; -use crate::vmm::hv::{CpuIo, IoBuf}; -use crate::vmm::hw::{DeviceContext, Ram}; +use crate::vmm::hv::{CpuIo, Hypervisor, IoBuf}; +use crate::vmm::hw::DeviceContext; use crate::vmm::VmmEvent; use obvirt::console::{Memory, MsgType}; use std::error::Error; @@ -8,18 +9,18 @@ use std::mem::offset_of; use thiserror::Error; /// Implementation of [`DeviceContext`]. -pub struct Context<'a> { +pub struct Context<'a, H> { dev: &'a Console, - ram: &'a Ram, + hv: &'a H, msg_len: usize, msg: String, } -impl<'a> Context<'a> { - pub fn new(dev: &'a Console, ram: &'a Ram) -> Self { +impl<'a, H: Hypervisor> Context<'a, H> { + pub fn new(dev: &'a Console, hv: &'a H) -> Self { Self { dev, - ram, + hv, msg_len: 0, msg: String::new(), } @@ -75,14 +76,14 @@ impl<'a> Context<'a> { .map_err(|e| ExecError::TranslateVaddrFailed(vaddr, e))?; // Read data. - let data = unsafe { self.ram.host_addr().add(paddr) }; + let data = unsafe { self.hv.ram().host_addr().add(paddr) }; let data = unsafe { std::slice::from_raw_parts(data, len) }; Ok(std::str::from_utf8(data).unwrap()) } } -impl<'a> DeviceContext for Context<'a> { +impl<'a, H: Hypervisor> DeviceContext for Context<'a, H> { fn exec(&mut self, exit: &mut dyn CpuIo) -> Result> { // Check field. let off = exit.addr() - self.dev.addr; diff --git a/src/core/src/vmm/hw/console/mod.rs b/src/core/src/vmm/hw/console/mod.rs index f8f459ab6..a12575506 100644 --- a/src/core/src/vmm/hw/console/mod.rs +++ b/src/core/src/vmm/hw/console/mod.rs @@ -1,5 +1,7 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 use self::context::Context; -use super::{Device, DeviceContext, Ram}; +use super::{Device, DeviceContext}; +use crate::vmm::hv::Hypervisor; use crate::vmm::VmmEventHandler; use obvirt::console::Memory; use std::num::NonZero; @@ -26,7 +28,7 @@ impl Console { } } -impl Device for Console { +impl Device for Console { fn addr(&self) -> usize { self.addr } @@ -35,7 +37,7 @@ impl Device for Console { self.len } - fn create_context<'a>(&'a self, ram: &'a Ram) -> Box { - Box::new(Context::new(self, ram)) + fn create_context<'a>(&'a self, hv: &'a H) -> Box { + Box::new(Context::new(self, hv)) } } diff --git a/src/core/src/vmm/hw/mod.rs b/src/core/src/vmm/hw/mod.rs index b4278f44e..23a7dbda7 100644 --- a/src/core/src/vmm/hw/mod.rs +++ b/src/core/src/vmm/hw/mod.rs @@ -1,28 +1,29 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +use super::hv::{CpuIo, Hypervisor}; use super::VmmEventHandler; -use crate::vmm::hv::CpuIo; use std::collections::BTreeMap; use std::error::Error; use std::num::NonZero; use std::sync::Arc; pub use self::console::*; -pub use self::ram::*; mod console; -mod ram; -pub fn setup_devices( +pub fn setup_devices( start_addr: usize, block_size: NonZero, event: VmmEventHandler, -) -> DeviceTree { - let mut map = BTreeMap::>::new(); +) -> DeviceTree { + let mut map = BTreeMap::>>::new(); // Console. let addr = start_addr; let console = Arc::new(Console::new(addr, block_size, event)); - assert!(map.insert(console.addr(), console.clone()).is_none()); + assert!(map + .insert(>::addr(&console), console.clone()) + .is_none()); // Make sure nothing are overlapped. let mut end = start_addr; @@ -36,31 +37,31 @@ pub fn setup_devices( } /// Contains all virtual devices, except RAM; for the VM. -pub struct DeviceTree { +pub struct DeviceTree { console: Arc, - map: BTreeMap>, + map: BTreeMap>>, } -impl DeviceTree { - pub fn console(&self) -> &Console { - &self.console +impl DeviceTree { + pub fn console(&self) -> &impl Device { + self.console.as_ref() } /// Returns iterator ordered by physical address. - pub fn map(&self) -> impl Iterator + '_ { + pub fn map(&self) -> impl Iterator)> + '_ { self.map.iter().map(|(addr, dev)| (*addr, dev.as_ref())) } } /// Virtual device that has a physical address in the virtual machine. -pub trait Device: Send + Sync { +pub trait Device: Send + Sync { /// Physical address in the virtual machine. fn addr(&self) -> usize; /// Total size of device memory, in bytes. fn len(&self) -> NonZero; - fn create_context<'a>(&'a self, ram: &'a Ram) -> Box; + fn create_context<'a>(&'a self, hv: &'a H) -> Box; } /// Context to execute memory-mapped I/O operations on a virtual device. diff --git a/src/core/src/vmm/mod.rs b/src/core/src/vmm/mod.rs index e640383e5..9a5959f63 100644 --- a/src/core/src/vmm/mod.rs +++ b/src/core/src/vmm/mod.rs @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use self::hv::{Cpu, CpuExit, CpuFeats, CpuIo, Hypervisor}; -use self::hw::{setup_devices, Device, DeviceContext, DeviceTree, Ram, RamBuilder, RamMap}; +use self::hw::{setup_devices, Device, DeviceContext, DeviceTree}; use self::kernel::{ Kernel, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_LOAD, PT_NOTE, PT_PHDR, }; +use self::ram::{Ram, RamMap}; use self::screen::Screen; use crate::error::RustError; use crate::profile::Profile; @@ -28,6 +29,7 @@ mod arch; mod hv; mod hw; mod kernel; +mod ram; mod screen; #[no_mangle] @@ -312,16 +314,35 @@ pub unsafe extern "C" fn vmm_run( }, }; - // Setup RAM builder. - let mut ram = match RamBuilder::new(block_size) { + // Setup RAM. + let ram = match Ram::new(block_size) { Ok(v) => v, Err(e) => { - *err = RustError::wrap(e); + *err = RustError::with_source("couldn't create a RAM", e); + return null_mut(); + } + }; + + // Setup hypervisor. + let mut hv = match self::hv::new(8, ram) { + Ok(v) => v, + Err(e) => { + *err = RustError::with_source("couldn't setup a hypervisor", e); + return null_mut(); + } + }; + + // Load CPU features. + let feats = match hv.cpu_features() { + Ok(v) => v, + Err(e) => { + *err = RustError::with_source("couldn't get available vCPU features", e); return null_mut(); } }; // Map the kernel. + let mut ram = hv.ram_mut().builder(); let kern = match ram.alloc_kernel(len) { Ok(v) => v, Err(e) => { @@ -385,7 +406,7 @@ pub unsafe extern "C" fn vmm_run( } // Build RAM. - let (ram, map) = match ram.build(vm_page_size, &devices, dynamic) { + let map = match ram.build(vm_page_size, &devices, dynamic) { Ok(v) => v, Err(e) => { *err = RustError::with_source("couldn't build RAM", e); @@ -393,25 +414,6 @@ pub unsafe extern "C" fn vmm_run( } }; - // Setup hypervisor. - let ram = Arc::new(ram); - let mut hv = match self::hv::new(8, ram.clone()) { - Ok(v) => v, - Err(e) => { - *err = RustError::with_source("couldn't setup a hypervisor", e); - return null_mut(); - } - }; - - // Load CPU features. - let feats = match hv.cpu_features() { - Ok(v) => v, - Err(e) => { - *err = RustError::with_source("couldn't get available vCPU features", e); - return null_mut(); - } - }; - // Setup screen. let screen = match self::screen::Default::new(screen) { Ok(v) => v, @@ -425,7 +427,6 @@ pub unsafe extern "C" fn vmm_run( let shutdown = Arc::new(AtomicBool::new(false)); let args = CpuArgs { hv, - ram, screen: screen.buffer().clone(), feats, devices, @@ -514,7 +515,7 @@ fn run_cpu(mut cpu: C, args: &CpuArgs) { .map(|(addr, dev)| { let end = dev.len().checked_add(addr).unwrap(); - (addr, (dev.create_context(&args.ram), end)) + (addr, (dev.create_context(&args.hv), end)) }) .collect::, NonZero)>>(); @@ -666,19 +667,15 @@ impl From for VmmLog { /// Encapsulates arguments for a function to run a CPU. struct CpuArgs { hv: H, - ram: Arc, screen: Arc<::Buffer>, feats: CpuFeats, - devices: Arc, + devices: Arc>, shutdown: Arc, } /// Represents an error when [`vmm_new()`] fails. #[derive(Debug, Error)] enum VmmError { - #[error("couldn't create a RAM")] - CreateRamFailed(#[source] std::io::Error), - #[cfg(target_os = "linux")] #[error("couldn't get maximum number of CPU for a VM")] GetMaxCpuFailed(#[source] std::io::Error), diff --git a/src/core/src/vmm/hw/ram/builder.rs b/src/core/src/vmm/ram/builder.rs similarity index 91% rename from src/core/src/vmm/hw/ram/builder.rs rename to src/core/src/vmm/ram/builder.rs index 8e63487d0..da892bc45 100644 --- a/src/core/src/vmm/hw/ram/builder.rs +++ b/src/core/src/vmm/ram/builder.rs @@ -1,71 +1,31 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::{Ram, RamError}; +use crate::vmm::hv::Hypervisor; use crate::vmm::hw::DeviceTree; use crate::vmm::kernel::ProgramHeader; -use crate::vmm::VmmError; use obconf::{BootEnv, Config}; use std::num::NonZero; use std::ops::Range; use thiserror::Error; /// Struct to build [`Ram`]. -pub struct RamBuilder { - ram: Ram, +pub struct RamBuilder<'a> { + ram: &'a mut Ram, next: usize, kern: Option>, stack: Option>, args: Option, } -impl RamBuilder { - /// # Safety - /// `block_size` must be greater or equal host page size. - pub unsafe fn new(block_size: NonZero) -> Result { - use std::io::Error; - - // Reserve memory range. - #[cfg(unix)] - let mem = { - use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; - use std::ptr::null_mut; - - let mem = mmap( - null_mut(), - Ram::SIZE, - PROT_NONE, - MAP_PRIVATE | MAP_ANON, - -1, - 0, - ); - - if mem == MAP_FAILED { - return Err(VmmError::CreateRamFailed(Error::last_os_error())); - } - - mem.cast() - }; - - #[cfg(windows)] - let mem = { - use std::ptr::null; - use windows_sys::Win32::System::Memory::{VirtualAlloc, MEM_RESERVE, PAGE_NOACCESS}; - - let mem = VirtualAlloc(null(), Ram::SIZE, MEM_RESERVE, PAGE_NOACCESS); - - if mem.is_null() { - return Err(VmmError::CreateRamFailed(Error::last_os_error())); - } - - mem.cast() - }; - - Ok(Self { - ram: Ram { mem, block_size }, +impl<'a> RamBuilder<'a> { + pub(super) fn new(ram: &'a mut Ram) -> Self { + Self { + ram, next: 0, kern: None, stack: None, args: None, - }) + } } /// # Panics @@ -212,13 +172,13 @@ impl RamBuilder { } #[cfg(target_arch = "x86_64")] -impl RamBuilder { - pub fn build( +impl<'a> RamBuilder<'a> { + pub fn build( mut self, page_size: NonZero, - devices: &DeviceTree, + devices: &DeviceTree, dynamic: ProgramHeader, - ) -> Result<(Ram, RamMap), RamBuilderError> { + ) -> Result { // Allocate page-map level-4 table. We use 4K 4-Level Paging here. You may wonder about this // because it seems like page size on the PS4 is 16K. The truth is the PS4 emulate the 16K // page size with 4K pages. You can check this by yourself by looking at @@ -292,7 +252,7 @@ impl RamBuilder { unsafe { self.relocate_kernel(&map, dynamic, 8)? }; - Ok((self.ram, map)) + Ok(map) } fn setup_4k_page_tables( @@ -393,17 +353,17 @@ impl RamBuilder { } #[cfg(target_arch = "aarch64")] -impl RamBuilder { +impl<'a> RamBuilder<'a> { const MA_DEV_NG_NR_NE: u8 = 0; // MEMORY_ATTRS[0] const MA_NOR: u8 = 1; // MEMORY_ATTRS[1] const MEMORY_ATTRS: [u8; 8] = [0, 0b11111111, 0, 0, 0, 0, 0, 0]; - pub fn build( + pub fn build( mut self, page_size: NonZero, - devices: &DeviceTree, + devices: &DeviceTree, dynamic: ProgramHeader, - ) -> Result<(Ram, RamMap), RamBuilderError> { + ) -> Result { // Setup page tables. let map = match page_size.get() { 0x4000 => self.build_16k_page_tables(devices)?, @@ -413,10 +373,13 @@ impl RamBuilder { // Relocate the kernel to virtual address. unsafe { self.relocate_kernel(&map, dynamic, 1027)? }; - Ok((self.ram, map)) + Ok(map) } - fn build_16k_page_tables(&mut self, devices: &DeviceTree) -> Result { + fn build_16k_page_tables( + &mut self, + devices: &DeviceTree, + ) -> Result { // Allocate page table level 0. let page_table = self.next; let len = self.ram.block_size; diff --git a/src/core/src/vmm/hw/ram/mod.rs b/src/core/src/vmm/ram/mod.rs similarity index 79% rename from src/core/src/vmm/hw/ram/mod.rs rename to src/core/src/vmm/ram/mod.rs index 31303e77a..ec9db8c9f 100644 --- a/src/core/src/vmm/hw/ram/mod.rs +++ b/src/core/src/vmm/ram/mod.rs @@ -22,6 +22,50 @@ pub struct Ram { impl Ram { pub(crate) const SIZE: usize = 1024 * 1024 * 1024 * 8; // 8GB + /// # Safety + /// `block_size` must be greater or equal host page size. + pub unsafe fn new(block_size: NonZero) -> Result { + use std::io::Error; + + // Reserve memory range. + #[cfg(unix)] + let mem = { + use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; + use std::ptr::null_mut; + + let mem = mmap( + null_mut(), + Self::SIZE, + PROT_NONE, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ); + + if mem == MAP_FAILED { + return Err(Error::last_os_error()); + } + + mem.cast() + }; + + #[cfg(windows)] + let mem = { + use std::ptr::null; + use windows_sys::Win32::System::Memory::{VirtualAlloc, MEM_RESERVE, PAGE_NOACCESS}; + + let mem = VirtualAlloc(null(), Self::SIZE, MEM_RESERVE, PAGE_NOACCESS); + + if mem.is_null() { + return Err(Error::last_os_error()); + } + + mem.cast() + }; + + Ok(Self { mem, block_size }) + } + pub fn host_addr(&self) -> *const u8 { self.mem } @@ -30,6 +74,10 @@ impl Ram { Self::SIZE } + pub fn builder(&mut self) -> RamBuilder { + RamBuilder::new(self) + } + /// # Panics /// If `addr` or `len` is not multiply by block size. /// diff --git a/src/core/src/vmm/x86_64.rs b/src/core/src/vmm/x86_64.rs index 5477b145a..4b7ec6f87 100644 --- a/src/core/src/vmm/x86_64.rs +++ b/src/core/src/vmm/x86_64.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use super::hv::{Cpu, CpuFeats, CpuStates}; -use super::hw::RamMap; +use super::ram::RamMap; use super::MainCpuError; pub fn setup_main_cpu(