From dcbf2ac13098556ed143ad5891da59efe400ba32 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Sun, 24 Mar 2024 22:00:31 +0700 Subject: [PATCH] Moves hypervisor to kernel (#763) --- src/Cargo.toml | 1 - src/hv/Cargo.toml | 23 -- src/hv/src/lib.rs | 338 ------------------ src/kernel/Cargo.toml | 5 +- src/{hv => kernel}/build.rs | 6 +- src/kernel/src/dev/random.rs | 2 +- src/{hv/src => kernel/src/hv}/linux/kvm.cpp | 0 src/{hv/src => kernel/src/hv}/linux/mod.rs | 16 +- .../src/darwin.rs => kernel/src/hv/mac.rs} | 5 +- src/kernel/src/hv/mod.rs | 183 ++++++++++ src/kernel/src/hv/ram.rs | 105 ++++++ src/{hv/src => kernel/src/hv}/win32.rs | 54 ++- src/kernel/src/main.rs | 5 +- 13 files changed, 336 insertions(+), 407 deletions(-) delete mode 100644 src/hv/Cargo.toml delete mode 100644 src/hv/src/lib.rs rename src/{hv => kernel}/build.rs (64%) rename src/{hv/src => kernel/src/hv}/linux/kvm.cpp (100%) rename src/{hv/src => kernel/src/hv}/linux/mod.rs (78%) rename src/{hv/src/darwin.rs => kernel/src/hv/mac.rs} (90%) create mode 100644 src/kernel/src/hv/mod.rs create mode 100644 src/kernel/src/hv/ram.rs rename src/{hv/src => kernel/src/hv}/win32.rs (78%) diff --git a/src/Cargo.toml b/src/Cargo.toml index c5bfef866..568be57c4 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -7,7 +7,6 @@ members = [ "fs", "ftp", "gmtx", - "hv", "kernel", "llt", "macros", diff --git a/src/hv/Cargo.toml b/src/hv/Cargo.toml deleted file mode 100644 index 5897d7bba..000000000 --- a/src/hv/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "hv" -version = "0.1.0" -edition = "2021" - -[dependencies] -thiserror = "1.0.57" - -[target.'cfg(unix)'.dependencies] -libc = "0.2.153" - -[target.'cfg(windows)'.dependencies.windows-sys] -version = "0.52" -features = [ - "Win32", - "Win32_System", - "Win32_System_Hypervisor", - "Win32_System_Memory", - "Win32_System_SystemInformation" -] - -[build-dependencies] -cc = "1.0.90" diff --git a/src/hv/src/lib.rs b/src/hv/src/lib.rs deleted file mode 100644 index 63411bfce..000000000 --- a/src/hv/src/lib.rs +++ /dev/null @@ -1,338 +0,0 @@ -use std::num::NonZeroUsize; -use std::sync::atomic::{AtomicBool, Ordering}; -use thiserror::Error; - -#[cfg(target_os = "macos")] -mod darwin; -#[cfg(any(target_os = "linux", target_os = "android"))] -mod linux; -#[cfg(target_os = "windows")] -mod win32; - -/// Manage a virtual machine of the current process. -/// -/// Each process can have only one VM. The reason this type is not a global variable is because we -/// want to be able to drop it. -pub struct Hypervisor { - #[cfg(any(target_os = "linux", target_os = "android"))] - vm: std::os::fd::OwnedFd, // Drop before KVM. - #[cfg(any(target_os = "linux", target_os = "android"))] - kvm: std::os::fd::OwnedFd, - #[cfg(target_os = "windows")] - whp: self::win32::Partition, - #[cfg(target_os = "macos")] - vm: self::darwin::Vm, - #[allow(dead_code)] - active: Active, // Drop as the last one. -} - -impl Hypervisor { - /// # Safety - /// `ram` cannot be null and must be allocated with a Virtual Memory API (e.g. `mmap` on *nix or - /// `VirtualAlloc` on Windows). This memory must be valid throughout the lifetime of the VM. - pub unsafe fn new( - cpu: NonZeroUsize, - ram: *mut u8, - addr: usize, - len: NonZeroUsize, - ) -> Result { - // Check if another instance already active. - let active = Active::new().ok_or(NewError::Active)?; - - // Make sure memory size is valid. - let host_page_size = match Self::host_page_size() { - #[cfg(unix)] - Ok(v) => v, - #[cfg(unix)] - Err(e) => return Err(NewError::GetHostPageSizeFailed(e)), - #[cfg(windows)] - v => v, - }; - - if len.get() % host_page_size != 0 { - return Err(NewError::InvalidMemorySize); - } - - // Initialize platform hypervisor. - #[cfg(any(target_os = "linux", target_os = "android"))] - return Self::new_linux(active, cpu, ram, addr, len.get()); - - #[cfg(target_os = "windows")] - return Self::new_windows(active, cpu, ram, addr, len.get()); - - #[cfg(target_os = "macos")] - return Self::new_mac(active, cpu, ram, addr, len.get()); - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - unsafe fn new_linux( - active: Active, - cpu: NonZeroUsize, - ram: *mut u8, - addr: usize, - len: usize, - ) -> Result { - use std::os::fd::AsFd; - - // Open KVM device. - let kvm = self::linux::open_kvm()?; - - if cpu.get() > self::linux::max_vcpus(kvm.as_fd()).map_err(NewError::GetMaxCpuFailed)? { - return Err(NewError::InvalidCpuCount); - } - - // Create a new VM. - let vm = self::linux::create_vm(kvm.as_fd()).map_err(NewError::CreateVmFailed)?; - - self::linux::set_user_memory_region( - vm.as_fd(), - 0, - addr.try_into().unwrap(), - len.try_into().unwrap(), - ram.cast(), - ) - .map_err(NewError::MapMemoryFailed)?; - - Ok(Self { vm, kvm, active }) - } - - #[cfg(target_os = "windows")] - unsafe fn new_windows( - active: Active, - cpu: NonZeroUsize, - ram: *mut u8, - addr: usize, - len: usize, - ) -> Result { - // Setup a partition. - let mut whp = self::win32::Partition::new(cpu)?; - - whp.setup().map_err(NewError::SetupPartitionFailed)?; - - // Map memory. - whp.map_gpa( - ram.cast(), - addr.try_into().unwrap(), - len.try_into().unwrap(), - ) - .map_err(NewError::MapMemoryFailed)?; - - Ok(Self { whp, active }) - } - - #[cfg(target_os = "macos")] - unsafe fn new_mac( - active: Active, - cpu: NonZeroUsize, - ram: *mut u8, - addr: usize, - len: usize, - ) -> Result { - // Create a VM. - let vm = self::darwin::Vm::new()?; - let cpu: u64 = cpu.get().try_into().unwrap(); - - if cpu > vm.capability(0).map_err(NewError::GetMaxCpuFailed)? { - return Err(NewError::InvalidCpuCount); - } - - // Map memory. - vm.vm_map(ram.cast(), addr.try_into().unwrap(), len) - .map_err(NewError::MapMemoryFailed)?; - - Ok(Self { vm, active }) - } - - #[cfg(unix)] - fn host_page_size() -> Result { - let v = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }; - - if v < 0 { - Err(std::io::Error::last_os_error()) - } else { - Ok(v.try_into().unwrap()) - } - } - - #[cfg(windows)] - fn host_page_size() -> usize { - use windows_sys::Win32::System::SystemInformation::GetSystemInfo; - - let mut i = unsafe { std::mem::zeroed() }; - unsafe { GetSystemInfo(&mut i) }; - - i.dwPageSize.try_into().unwrap() - } -} - -/// RAII object to set release ACTIVE. -struct Active; - -impl Active { - fn new() -> Option { - ACTIVE - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .map(|_| Self) - .ok() - } -} - -impl Drop for Active { - fn drop(&mut self) { - ACTIVE.store(false, Ordering::Release); - } -} - -/// Represents an error when [`Hypervisor::new()`] fails. -#[derive(Debug, Error)] -pub enum NewError { - #[error("there is an active hypervisor")] - Active, - - #[cfg(unix)] - #[error("couldn't determine page size of the host")] - GetHostPageSizeFailed(#[source] std::io::Error), - - #[error("the number of CPU is not valid")] - InvalidCpuCount, - - #[error("the specified memory size is not valid")] - InvalidMemorySize, - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("couldn't open {0}")] - OpenKvmFailed(&'static str, #[source] std::io::Error), - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("couldn't get KVM version")] - GetKvmVersionFailed(#[source] std::io::Error), - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("unexpected KVM version")] - KvmVersionMismatched, - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("couldn't get maximum number of CPU for a VM")] - GetMaxCpuFailed(#[source] std::io::Error), - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("couldn't create a VM")] - CreateVmFailed(#[source] std::io::Error), - - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("couldn't map a VM memory")] - MapMemoryFailed(#[source] std::io::Error), - - #[cfg(target_os = "windows")] - #[error("couldn't create WHP partition object ({0:#x})")] - CreatePartitionFailed(windows_sys::core::HRESULT), - - #[cfg(target_os = "windows")] - #[error("couldn't set number of CPU ({0:#x})")] - SetCpuCountFailed(windows_sys::core::HRESULT), - - #[cfg(target_os = "windows")] - #[error("couldn't setup WHP partition ({0:#x})")] - SetupPartitionFailed(windows_sys::core::HRESULT), - - #[cfg(target_os = "windows")] - #[error("couldn't map memory to WHP partition ({0:#x})")] - MapMemoryFailed(windows_sys::core::HRESULT), - - #[cfg(target_os = "macos")] - #[error("couldn't create a VM ({0:#x})")] - CreateVmFailed(std::ffi::c_int), - - #[cfg(target_os = "macos")] - #[error("couldn't get maximum number of CPU for a VM")] - GetMaxCpuFailed(std::ffi::c_int), - - #[cfg(target_os = "macos")] - #[error("couldn't map memory to the VM")] - MapMemoryFailed(std::ffi::c_int), -} - -static ACTIVE: AtomicBool = AtomicBool::new(false); - -// macOS requires additional entitlements for the application to use Hypervisor framework, which -// cannot be done with "cargo test". -#[cfg(not(target_os = "macos"))] -#[cfg(test)] -mod tests { - use super::*; - use std::io::Error; - - #[test] - fn new() { - let cpu = unsafe { NonZeroUsize::new_unchecked(8) }; - let ram = Ram::new(); - - unsafe { Hypervisor::new(cpu, ram.addr, 0, Ram::SIZE).unwrap() }; - } - - struct Ram { - addr: *mut u8, - } - - impl Ram { - const SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1024 * 1024 * 1024 * 8) }; - - #[cfg(unix)] - fn new() -> Self { - use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; - use std::ptr::null_mut; - - let addr = unsafe { - mmap( - null_mut(), - Self::SIZE.get(), - PROT_NONE, - MAP_PRIVATE | MAP_ANON, - -1, - 0, - ) - }; - - if addr == MAP_FAILED { - panic!("mmap() fails: {}", Error::last_os_error()); - } - - Self { addr: addr.cast() } - } - - #[cfg(windows)] - fn new() -> Self { - use std::ptr::null; - use windows_sys::Win32::System::Memory::{VirtualAlloc, MEM_RESERVE, PAGE_NOACCESS}; - - let addr = - unsafe { VirtualAlloc(null(), Self::SIZE.get(), MEM_RESERVE, PAGE_NOACCESS) }; - - if addr.is_null() { - panic!("VirtualAlloc() fails: {}", Error::last_os_error()); - } - - Self { addr: addr.cast() } - } - } - - impl Drop for Ram { - #[cfg(unix)] - fn drop(&mut self) { - use libc::munmap; - - if unsafe { munmap(self.addr.cast(), Self::SIZE.get()) } < 0 { - panic!("munmap() fails: {}", Error::last_os_error()); - } - } - - #[cfg(windows)] - fn drop(&mut self) { - use windows_sys::Win32::System::Memory::{VirtualFree, MEM_RELEASE}; - - if unsafe { VirtualFree(self.addr.cast(), 0, MEM_RELEASE) } == 0 { - panic!("VirtualFree() fails: {}", Error::last_os_error()); - } - } - } -} diff --git a/src/kernel/Cargo.toml b/src/kernel/Cargo.toml index 73a3b63d7..0d357fad6 100644 --- a/src/kernel/Cargo.toml +++ b/src/kernel/Cargo.toml @@ -15,7 +15,6 @@ clap = { version = "4.1", features = ["derive"] } discord-rich-presence = "0.2" elf = { path = "../elf" } gmtx = { path = "../gmtx" } -hv = { path = "../hv" } iced-x86 = { version = "1.18", features = ["code_asm"] } libc = "0.2" llt = { path = "../llt" } @@ -42,6 +41,7 @@ features = [ "Win32_Storage_FileSystem", "Win32_System", "Win32_System_Diagnostics_Debug", + "Win32_System_Hypervisor", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Memory", @@ -50,3 +50,6 @@ features = [ "Win32_System_Time", "Win32_System_WindowsProgramming", ] + +[build-dependencies] +cc = "1.0.90" diff --git a/src/hv/build.rs b/src/kernel/build.rs similarity index 64% rename from src/hv/build.rs rename to src/kernel/build.rs index 406cb0231..010eb8c89 100644 --- a/src/hv/build.rs +++ b/src/kernel/build.rs @@ -1,12 +1,12 @@ fn main() { match std::env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { "linux" | "android" => { - println!("cargo::rerun-if-changed=src/linux/kvm.cpp"); + println!("cargo::rerun-if-changed=src/hv/linux/kvm.cpp"); cc::Build::new() .cpp(true) - .file("src/linux/kvm.cpp") - .compile("obhv"); + .file("src/hv/linux/kvm.cpp") + .compile("obkrnlffi"); } "macos" => println!("cargo:rustc-link-lib=framework=Hypervisor"), _ => {} diff --git a/src/kernel/src/dev/random.rs b/src/kernel/src/dev/random.rs index 43bf47fa8..75a0e3214 100644 --- a/src/kernel/src/dev/random.rs +++ b/src/kernel/src/dev/random.rs @@ -31,7 +31,7 @@ impl DeviceDriver for Random { fn ioctl( &self, - dev: &Arc, + _: &Arc, cmd: IoCmd, _: &VThread, ) -> Result<(), Box> { diff --git a/src/hv/src/linux/kvm.cpp b/src/kernel/src/hv/linux/kvm.cpp similarity index 100% rename from src/hv/src/linux/kvm.cpp rename to src/kernel/src/hv/linux/kvm.cpp diff --git a/src/hv/src/linux/mod.rs b/src/kernel/src/hv/linux/mod.rs similarity index 78% rename from src/hv/src/linux/mod.rs rename to src/kernel/src/hv/linux/mod.rs index d37a01ddc..986524774 100644 --- a/src/hv/src/linux/mod.rs +++ b/src/kernel/src/hv/linux/mod.rs @@ -1,15 +1,15 @@ -use crate::NewError; +use super::HypervisorError; use libc::{open, O_RDWR}; use std::ffi::{c_int, c_void}; use std::io::Error; use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; -pub fn open_kvm() -> Result { +pub fn open_kvm() -> Result { // Open KVM. - let fd = unsafe { open(b"/dev/kvm\0".as_ptr().cast(), O_RDWR) }; + let fd = unsafe { open(c"/dev/kvm".as_ptr(), O_RDWR) }; if fd < 0 { - return Err(NewError::OpenKvmFailed("/dev/kvm", Error::last_os_error())); + return Err(HypervisorError::OpenKvmFailed(Error::last_os_error())); } // Check KVM version. @@ -19,10 +19,14 @@ pub fn open_kvm() -> Result { match unsafe { kvm_check_version(fd.as_raw_fd(), &mut compat) } { 0 => { if !compat { - return Err(NewError::KvmVersionMismatched); + return Err(HypervisorError::KvmVersionMismatched); } } - v => return Err(NewError::GetKvmVersionFailed(Error::from_raw_os_error(v))), + v => { + return Err(HypervisorError::GetKvmVersionFailed( + Error::from_raw_os_error(v), + )) + } } Ok(fd) diff --git a/src/hv/src/darwin.rs b/src/kernel/src/hv/mac.rs similarity index 90% rename from src/hv/src/darwin.rs rename to src/kernel/src/hv/mac.rs index 1418c22d4..7cfecbd9b 100644 --- a/src/hv/src/darwin.rs +++ b/src/kernel/src/hv/mac.rs @@ -1,4 +1,3 @@ -use crate::NewError; use std::ffi::{c_int, c_void}; use std::ptr::null_mut; @@ -6,10 +5,10 @@ use std::ptr::null_mut; pub struct Vm(()); impl Vm { - pub fn new() -> Result { + pub fn new() -> Result { match unsafe { hv_vm_create(null_mut()) } { 0 => Ok(Self(())), - v => Err(NewError::CreateVmFailed(v)), + v => Err(v), } } diff --git a/src/kernel/src/hv/mod.rs b/src/kernel/src/hv/mod.rs new file mode 100644 index 000000000..184df616d --- /dev/null +++ b/src/kernel/src/hv/mod.rs @@ -0,0 +1,183 @@ +pub use self::ram::*; +use thiserror::Error; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "macos")] +mod mac; +mod ram; +#[cfg(target_os = "windows")] +mod win32; + +/// Manage a virtual machine for running the PS4 processes. +/// +/// Do not create more than one Hypervisor because it will not work macOS. +pub struct Hypervisor { + #[cfg(target_os = "linux")] + vm: std::os::fd::OwnedFd, // Drop before KVM. + #[cfg(target_os = "linux")] + kvm: std::os::fd::OwnedFd, + #[cfg(target_os = "windows")] + part: self::win32::Partition, + #[cfg(target_os = "macos")] + vm: self::mac::Vm, + ram: Ram, // Drop after a VM. +} + +impl Hypervisor { + pub fn new() -> Result { + let ram = Ram::new(0).map_err(HypervisorError::CreateRamFailed)?; + + // Initialize platform hypervisor. + #[cfg(target_os = "linux")] + return Self::new_linux(ram); + + #[cfg(target_os = "windows")] + return Self::new_windows(ram); + + #[cfg(target_os = "macos")] + return Self::new_mac(ram); + } + + #[cfg(target_os = "linux")] + fn new_linux(ram: Ram) -> Result { + use std::os::fd::AsFd; + + // Open KVM device. + let kvm = self::linux::open_kvm()?; + + if self::linux::max_vcpus(kvm.as_fd()).map_err(HypervisorError::GetMaxCpuFailed)? < 8 { + return Err(HypervisorError::MaxCpuTooLow); + } + + // Create a new VM. + let vm = self::linux::create_vm(kvm.as_fd()).map_err(HypervisorError::CreateVmFailed)?; + + self::linux::set_user_memory_region( + vm.as_fd(), + 0, + ram.vm_addr().try_into().unwrap(), + ram.len().try_into().unwrap(), + ram.host_addr().cast(), + ) + .map_err(HypervisorError::MapRamFailed)?; + + Ok(Self { vm, kvm, ram }) + } + + #[cfg(target_os = "windows")] + fn new_windows(ram: Ram) -> Result { + // Setup a partition. + let mut part = + self::win32::Partition::new().map_err(HypervisorError::CreatePartitionFailed)?; + + part.set_vcpu(8) + .map_err(HypervisorError::SetCpuCountFailed)?; + part.setup() + .map_err(HypervisorError::SetupPartitionFailed)?; + + // Map memory. + part.map_gpa( + ram.host_addr().cast(), + ram.vm_addr().try_into().unwrap(), + ram.len().try_into().unwrap(), + ) + .map_err(HypervisorError::MapRamFailed)?; + + Ok(Self { part, ram }) + } + + #[cfg(target_os = "macos")] + fn new_mac(ram: Ram) -> Result { + // Create a VM. + let vm = self::mac::Vm::new().map_err(HypervisorError::CreateVmFailed)?; + + if vm.capability(0).map_err(HypervisorError::GetMaxCpuFailed)? < 8 { + return Err(HypervisorError::MaxCpuTooLow); + } + + // Map memory. + vm.vm_map( + ram.host_addr().cast(), + ram.vm_addr().try_into().unwrap(), + ram.len(), + ) + .map_err(HypervisorError::MapRamFailed)?; + + Ok(Self { vm, ram }) + } +} + +/// Object that has a physical address in the virtual machine. +pub trait MemoryAddr { + /// Physical address in the virtual machine. + fn vm_addr(&self) -> usize; + + /// Address in our process. + fn host_addr(&self) -> *mut (); + + /// Total size of the object, in bytes. + fn len(&self) -> usize; +} + +/// Represents an error when [`Hypervisor::new()`] fails. +#[derive(Debug, Error)] +pub enum HypervisorError { + #[error("couldn't create a RAM")] + CreateRamFailed(#[source] std::io::Error), + + #[error("your OS does not support 8 vCPU on a VM")] + MaxCpuTooLow, + + #[cfg(target_os = "linux")] + #[error("couldn't open /dev/kvm")] + OpenKvmFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("couldn't get KVM version")] + GetKvmVersionFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("unexpected KVM version")] + KvmVersionMismatched, + + #[cfg(target_os = "linux")] + #[error("couldn't get maximum number of CPU for a VM")] + GetMaxCpuFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("couldn't create a VM")] + CreateVmFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("couldn't map the RAM to the VM")] + MapRamFailed(#[source] std::io::Error), + + #[cfg(target_os = "windows")] + #[error("couldn't create WHP partition object ({0:#x})")] + CreatePartitionFailed(windows_sys::core::HRESULT), + + #[cfg(target_os = "windows")] + #[error("couldn't set number of CPU ({0:#x})")] + SetCpuCountFailed(windows_sys::core::HRESULT), + + #[cfg(target_os = "windows")] + #[error("couldn't setup WHP partition ({0:#x})")] + SetupPartitionFailed(windows_sys::core::HRESULT), + + #[cfg(target_os = "windows")] + #[error("couldn't map the RAM to WHP partition ({0:#x})")] + MapRamFailed(windows_sys::core::HRESULT), + + #[cfg(target_os = "macos")] + #[error("couldn't create a VM ({0:#x})")] + CreateVmFailed(std::ffi::c_int), + + #[cfg(target_os = "macos")] + #[error("couldn't get maximum number of CPU for a VM")] + GetMaxCpuFailed(std::ffi::c_int), + + #[cfg(target_os = "macos")] + #[error("couldn't map memory to the VM")] + MapRamFailed(std::ffi::c_int), +} diff --git a/src/kernel/src/hv/ram.rs b/src/kernel/src/hv/ram.rs new file mode 100644 index 000000000..025b08072 --- /dev/null +++ b/src/kernel/src/hv/ram.rs @@ -0,0 +1,105 @@ +use super::MemoryAddr; +use std::io::Error; + +/// Represents main memory of the PS4. +/// +/// This struct will allocate a 8GB of memory immediately but not commit any parts of it until there +/// is an allocation request. That mean the actual memory usage is not fixed at 8GB but will be +/// depend on what PS4 applications currently running. If it is a simple game the memory usage might +/// just a hundred of megabytes. +pub struct Ram { + addr: usize, + mem: *mut u8, +} + +impl Ram { + pub const SIZE: usize = 1024 * 1024 * 1024 * 8; // 8GB + + pub fn new(addr: usize) -> Result { + // Reserve a memory range on *nix. + #[cfg(unix)] + let mem = { + use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; + use std::ptr::null_mut; + + let mem = unsafe { + 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() + }; + + // Reserve a memory range on Windows. + #[cfg(windows)] + let mem = { + use std::ptr::null; + use windows_sys::Win32::System::Memory::{VirtualAlloc, MEM_RESERVE, PAGE_NOACCESS}; + + let mem = unsafe { VirtualAlloc(null(), Self::SIZE, MEM_RESERVE, PAGE_NOACCESS) }; + + if mem.is_null() { + return Err(Error::last_os_error()); + } + + mem.cast() + }; + + Ok(Self { addr, mem }) + } +} + +impl Drop for Ram { + #[cfg(unix)] + fn drop(&mut self) { + use libc::munmap; + + if unsafe { munmap(self.mem.cast(), Self::SIZE) } < 0 { + panic!( + "failed to unmap RAM at {:p}: {}", + self.mem, + Error::last_os_error() + ); + } + } + + #[cfg(windows)] + fn drop(&mut self) { + use windows_sys::Win32::System::Memory::{VirtualFree, MEM_RELEASE}; + + if unsafe { VirtualFree(self.mem.cast(), 0, MEM_RELEASE) } == 0 { + panic!( + "failed to free RAM at {:p}: {}", + self.mem, + Error::last_os_error() + ); + } + } +} + +impl MemoryAddr for Ram { + fn vm_addr(&self) -> usize { + self.addr + } + + fn host_addr(&self) -> *mut () { + self.mem.cast() + } + + fn len(&self) -> usize { + Self::SIZE + } +} + +unsafe impl Send for Ram {} +unsafe impl Sync for Ram {} diff --git a/src/hv/src/win32.rs b/src/kernel/src/hv/win32.rs similarity index 78% rename from src/hv/src/win32.rs rename to src/kernel/src/hv/win32.rs index 9dfdcae42..0b0e7a6bb 100644 --- a/src/hv/src/win32.rs +++ b/src/kernel/src/hv/win32.rs @@ -1,7 +1,5 @@ -use crate::NewError; use std::ffi::c_void; use std::mem::size_of; -use std::num::NonZeroUsize; use windows_sys::core::HRESULT; use windows_sys::Win32::System::Hypervisor::{ WHvCreatePartition, WHvDeletePartition, WHvMapGpaRange, WHvMapGpaRangeFlagExecute, @@ -14,49 +12,32 @@ use windows_sys::Win32::System::Hypervisor::{ pub struct Partition(WHV_PARTITION_HANDLE); impl Partition { - pub fn new(cpu: NonZeroUsize) -> Result { - let cpu = cpu - .get() - .try_into() - .map_err(|_| NewError::InvalidCpuCount)?; - - // Create a partition. + pub fn new() -> Result { let mut handle = 0; let status = unsafe { WHvCreatePartition(&mut handle) }; if status < 0 { - return Err(NewError::CreatePartitionFailed(status)); + Err(status) + } else { + Ok(Self(handle)) } + } - // Set CPU count. - let mut part = Self(handle); + pub fn set_vcpu(&mut self, n: usize) -> Result<(), HRESULT> { let status = unsafe { - part.set_property( + self.set_property( WHvPartitionPropertyCodeProcessorCount, &WHV_PARTITION_PROPERTY { - ProcessorCount: cpu, + ProcessorCount: n.try_into().unwrap(), }, ) }; if status < 0 { - return Err(NewError::SetCpuCountFailed(status)); + Err(status) + } else { + Ok(()) } - - return Ok(part); - } - - pub unsafe fn set_property( - &mut self, - name: WHV_PARTITION_PROPERTY_CODE, - value: &WHV_PARTITION_PROPERTY, - ) -> HRESULT { - WHvSetPartitionProperty( - self.0, - name, - value as *const WHV_PARTITION_PROPERTY as *const c_void, - size_of::().try_into().unwrap(), - ) } pub fn setup(&mut self) -> Result<(), HRESULT> { @@ -86,6 +67,19 @@ impl Partition { Ok(()) } } + + unsafe fn set_property( + &mut self, + name: WHV_PARTITION_PROPERTY_CODE, + value: &WHV_PARTITION_PROPERTY, + ) -> HRESULT { + WHvSetPartitionProperty( + self.0, + name, + value as *const WHV_PARTITION_PROPERTY as *const c_void, + size_of::().try_into().unwrap(), + ) + } } impl Drop for Partition { diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index c1d613932..64a66e14e 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -6,6 +6,7 @@ use crate::ee::native::NativeEngine; use crate::ee::EntryArg; use crate::errno::EEXIST; use crate::fs::{Fs, FsInitError, MkdirError, MountError, MountFlags, MountOpts, VPath, VPathBuf}; +use crate::hv::Hypervisor; use crate::kqueue::KernelQueueManager; use crate::log::{print, LOGGER}; use crate::namedobj::NamedObjManager; @@ -44,6 +45,7 @@ mod dmem; mod ee; mod errno; mod fs; +mod hv; mod idt; mod kqueue; mod log; @@ -185,6 +187,7 @@ fn run() -> Result<(), KernelError> { )); // Initialize foundations. + let hv = Hypervisor::new()?; let mut syscalls = Syscalls::new(); let fs = Fs::new(args.system, &cred, &mut syscalls)?; @@ -623,7 +626,7 @@ enum KernelError { FailedToLoadLibSceLibcInternal(#[source] Box), #[error("couldn't create a hypervisor")] - CreateHypervisorFailed(#[from] hv::NewError), + CreateHypervisorFailed(#[from] hv::HypervisorError), #[error("main thread couldn't be created")] FailedToCreateMainThread(#[from] SpawnError),