From 78e4f92e96d018c4a5847e7d08ac3f1bfe9d939c Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Sun, 16 Jun 2024 21:51:36 -0700 Subject: [PATCH] feat: skeleton for running VMs on Apple silicon Signed-off-by: Changyuan Lyu --- alioth-cli/src/main.rs | 29 ++++- alioth/src/hv/hv.rs | 19 ++-- alioth/src/hv/hvf/hvf.rs | 200 +++++++++++++++++++++++++++++++++++ alioth/src/mem/mapped.rs | 16 ++- alioth/src/utils/utils.rs | 1 + alioth/src/virtio/dev/dev.rs | 3 + alioth/src/virtio/virtio.rs | 3 + 7 files changed, 257 insertions(+), 14 deletions(-) create mode 100644 alioth/src/hv/hvf/hvf.rs diff --git a/alioth-cli/src/main.rs b/alioth-cli/src/main.rs index 60e0225..ed405ae 100644 --- a/alioth-cli/src/main.rs +++ b/alioth-cli/src/main.rs @@ -17,12 +17,18 @@ use std::fs::File; use std::path::PathBuf; use alioth::board::BoardConfig; +#[cfg(target_os = "macos")] +use alioth::hv::Hvf; +#[cfg(target_os = "linux")] use alioth::hv::{Kvm, KvmConfig}; use alioth::loader::{ExecType, Payload}; use alioth::virtio::dev::blk::BlockParam; use alioth::virtio::dev::entropy::EntropyParam; +#[cfg(target_os = "linux")] use alioth::virtio::dev::fs::VuFsParam; +#[cfg(target_os = "linux")] use alioth::virtio::dev::net::NetParam; +#[cfg(target_os = "linux")] use alioth::virtio::dev::vsock::VhostVsockParam; use alioth::vm::Machine; use clap::{Args, Parser, Subcommand}; @@ -57,23 +63,34 @@ enum Command { #[derive(Debug, Deserialize, Clone)] enum Hypervisor { + #[cfg(target_os = "linux")] #[serde(alias = "kvm")] Kvm(KvmConfig), + #[cfg(target_os = "macos")] + Hvf, } impl Default for Hypervisor { fn default() -> Self { #[cfg(target_os = "linux")] - Hypervisor::Kvm(KvmConfig::default()) + { + Hypervisor::Kvm(KvmConfig::default()) + } + #[cfg(target_os = "macos")] + { + Hypervisor::Hvf + } } } +#[cfg(target_os = "linux")] #[derive(Debug, Deserialize, Clone)] enum FsParam { #[serde(alias = "vu")] Vu(VuFsParam), } +#[cfg(target_os = "linux")] #[derive(Debug, Deserialize, Clone)] enum VsockParam { #[serde(alias = "vhost")] @@ -169,8 +186,11 @@ fn main_run(args: RunArgs) -> Result<(), Error> { Hypervisor::default() }; let hypervisor = match hv_config { - Hypervisor::Kvm(kvm_config) => Kvm::new(kvm_config).context(error::Hypervisor), - }?; + #[cfg(target_os = "linux")] + Hypervisor::Kvm(kvm_config) => Kvm::new(kvm_config).context(error::Hypervisor)?, + #[cfg(target_os = "macos")] + Hypervisor::Hvf => Hvf {}, + }; let coco = match args.coco { None => None, Some(c) => Some(serde_aco::from_arg(&c).context(error::ParseArg { arg: c })?), @@ -227,6 +247,7 @@ fn main_run(args: RunArgs) -> Result<(), Error> { vm.add_virtio_dev("virtio-entropy".to_owned(), EntropyParam) .context(error::CreateDevice)?; } + #[cfg(target_os = "linux")] for (index, net_opt) in args.net.into_iter().enumerate() { let net_param: NetParam = serde_aco::from_arg(&net_opt).context(error::ParseArg { arg: net_opt })?; @@ -238,6 +259,7 @@ fn main_run(args: RunArgs) -> Result<(), Error> { vm.add_virtio_dev(format!("virtio-blk-{index}"), param) .context(error::CreateDevice)?; } + #[cfg(target_os = "linux")] for (index, fs) in args.fs.into_iter().enumerate() { let param: FsParam = serde_aco::from_arg(&fs).context(error::ParseArg { arg: fs })?; match param { @@ -246,6 +268,7 @@ fn main_run(args: RunArgs) -> Result<(), Error> { .context(error::CreateDevice)?, }; } + #[cfg(target_os = "linux")] if let Some(vsock) = args.vsock { let param = serde_aco::from_arg(&vsock).context(error::ParseArg { arg: vsock })?; match param { diff --git a/alioth/src/hv/hv.rs b/alioth/src/hv/hv.rs index 0278727..732feab 100644 --- a/alioth/src/hv/hv.rs +++ b/alioth/src/hv/hv.rs @@ -12,22 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(target_os = "macos")] +#[path = "hvf/hvf.rs"] +mod hvf; #[cfg(target_os = "linux")] #[path = "kvm/kvm.rs"] mod kvm; #[cfg(test)] pub(crate) mod test; -#[cfg(target_os = "linux")] -pub use kvm::{Kvm, KvmConfig, KvmError}; -use macros::trace_error; -use serde::Deserialize; -use snafu::Snafu; - use std::fmt::Debug; use std::os::fd::AsFd; use std::sync::Arc; use std::thread::JoinHandle; +use macros::trace_error; +use serde::Deserialize; +use snafu::Snafu; + #[cfg(target_arch = "x86_64")] use crate::arch::cpuid::Cpuid; use crate::arch::reg::Reg; @@ -36,6 +37,11 @@ use crate::arch::reg::{DtReg, DtRegVal, SReg, SegReg, SegRegVal}; #[cfg(target_arch = "x86_64")] use crate::arch::sev::{SevPolicy, SnpPageType, SnpPolicy}; +#[cfg(target_os = "macos")] +pub use hvf::Hvf; +#[cfg(target_os = "linux")] +pub use kvm::{Kvm, KvmConfig, KvmError}; + #[trace_error] #[derive(Snafu)] #[snafu(module, context(suffix(false)))] @@ -85,6 +91,7 @@ pub enum Error { RunVcpu { error: std::io::Error }, #[snafu(display("Failed to stop a VCPU"))] StopVcpu { error: std::io::Error }, + #[cfg(target_os = "linux")] #[snafu(display("KVM internal error"), context(false))] KvmErr { source: Box }, } diff --git a/alioth/src/hv/hvf/hvf.rs b/alioth/src/hv/hvf/hvf.rs new file mode 100644 index 0000000..26b9e6e --- /dev/null +++ b/alioth/src/hv/hvf/hvf.rs @@ -0,0 +1,200 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::os::fd::{AsFd, BorrowedFd}; +use std::thread::JoinHandle; + +use crate::arch::reg::Reg; +use crate::hv::{ + Hypervisor, IntxSender, IoeventFd, IoeventFdRegistry, IrqFd, MemMapOption, MsiSender, Result, + Vcpu, Vm, VmEntry, VmExit, VmMemory, +}; + +#[derive(Debug)] +pub struct HvfVcpu {} + +impl Vcpu for HvfVcpu { + fn dump(&self) -> Result<()> { + unimplemented!() + } + + fn get_reg(&self, _reg: Reg) -> Result { + unimplemented!() + } + + fn run(&mut self, _entry: VmEntry) -> Result { + unimplemented!() + } + + fn set_regs(&mut self, _vals: &[(Reg, u64)]) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfMemory {} + +impl VmMemory for HvfMemory { + fn deregister_encrypted_range(&self, _range: &[u8]) -> Result<()> { + unimplemented!() + } + fn max_mem_slots(&self) -> Result { + unimplemented!() + } + fn mem_map( + &self, + _slot: u32, + _gpa: usize, + _size: usize, + _hva: usize, + _option: MemMapOption, + ) -> Result<()> { + unimplemented!() + } + + fn register_encrypted_range(&self, _range: &[u8]) -> Result<()> { + unimplemented!() + } + + fn unmap(&self, _slot: u32, _gpa: usize, _size: usize) -> Result<()> { + unimplemented!() + } + + fn mark_private_memory(&self, _gpa: u64, _size: u64, _private: bool) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfIntxSender {} +impl IntxSender for HvfIntxSender { + fn send(&self) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfIrqFd {} +impl AsFd for HvfIrqFd { + fn as_fd(&self) -> BorrowedFd<'_> { + unimplemented!() + } +} +impl IrqFd for HvfIrqFd { + fn get_addr_hi(&self) -> u32 { + unimplemented!() + } + fn get_addr_lo(&self) -> u32 { + unimplemented!() + } + fn get_data(&self) -> u32 { + unimplemented!() + } + fn get_masked(&self) -> bool { + unimplemented!() + } + fn set_addr_hi(&self, _val: u32) -> Result<()> { + unimplemented!() + } + fn set_addr_lo(&self, _val: u32) -> Result<()> { + unimplemented!() + } + fn set_data(&self, _val: u32) -> Result<()> { + unimplemented!() + } + fn set_masked(&self, _val: bool) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfMsiSender {} + +impl MsiSender for HvfMsiSender { + type IrqFd = HvfIrqFd; + fn create_irqfd(&self) -> Result { + unimplemented!() + } + fn send(&self, _addr: u64, _data: u32) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfIoeventFd {} + +impl IoeventFd for HvfIoeventFd {} + +impl AsFd for HvfIoeventFd { + fn as_fd(&self) -> BorrowedFd<'_> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfIoeventFdRegistry {} + +impl IoeventFdRegistry for HvfIoeventFdRegistry { + type IoeventFd = HvfIoeventFd; + fn create(&self) -> Result { + unimplemented!() + } + fn deregister(&self, _fd: &Self::IoeventFd) -> Result<()> { + unimplemented!() + } + fn register( + &self, + _fd: &Self::IoeventFd, + _gpa: usize, + _len: u8, + _data: Option, + ) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct HvfVm {} + +impl Vm for HvfVm { + type Vcpu = HvfVcpu; + type Memory = HvfMemory; + type MsiSender = HvfMsiSender; + type IoeventFdRegistry = HvfIoeventFdRegistry; + fn create_ioeventfd_registry(&self) -> Result { + unimplemented!() + } + fn create_msi_sender(&self) -> Result { + unimplemented!() + } + fn create_vcpu(&self, _id: u32) -> Result { + unimplemented!() + } + fn create_vm_memory(&mut self) -> Result { + unimplemented!() + } + fn stop_vcpu(_id: u32, _handle: &JoinHandle) -> Result<()> { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct Hvf {} + +impl Hypervisor for Hvf { + type Vm = HvfVm; + fn create_vm(&self, _config: &super::VmConfig) -> Result { + unimplemented!() + } +} diff --git a/alioth/src/mem/mapped.rs b/alioth/src/mem/mapped.rs index 8a0f527..8a9d54c 100644 --- a/alioth/src/mem/mapped.rs +++ b/alioth/src/mem/mapped.rs @@ -13,20 +13,25 @@ // limitations under the License. use std::cell::UnsafeCell; +#[cfg(target_os = "linux")] use std::ffi::CStr; use std::fmt::Debug; use std::fs::File; use std::io::{IoSlice, IoSliceMut, Read, Write}; use std::mem::{align_of, size_of}; use std::ops::Deref; -use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd}; +#[cfg(target_os = "linux")] +use std::os::fd::FromRawFd; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd}; use std::ptr::{null_mut, NonNull}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; +#[cfg(target_os = "linux")] +use libc::MFD_CLOEXEC; use libc::{ - c_void, mmap, msync, munmap, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, MFD_CLOEXEC, - MS_ASYNC, PROT_EXEC, PROT_READ, PROT_WRITE, + c_void, mmap, msync, munmap, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, MS_ASYNC, + PROT_EXEC, PROT_READ, PROT_WRITE, }; use parking_lot::{RwLock, RwLockReadGuard}; use zerocopy::{AsBytes, FromBytes}; @@ -109,6 +114,7 @@ impl ArcMemPages { Ok(Self::from_raw(addr, len, Some(file))) } + #[cfg(target_os = "linux")] pub fn from_memfd(size: usize, prot: Option, name: Option<&CStr>) -> Result { let name = name.unwrap_or(c"anon"); let fd = ffi!(unsafe { libc::memfd_create(name.as_ptr(), MFD_CLOEXEC) })?; @@ -610,8 +616,8 @@ mod test { fn test_ram_bus_read() { let bus = RamBus::new(FakeVmMemory); let prot = PROT_READ | PROT_WRITE; - let mem1 = ArcMemPages::from_memfd(PAGE_SIZE, Some(prot), None).unwrap(); - let mem2 = ArcMemPages::from_memfd(PAGE_SIZE, Some(prot), None).unwrap(); + let mem1 = ArcMemPages::from_anonymous(PAGE_SIZE, Some(prot)).unwrap(); + let mem2 = ArcMemPages::from_anonymous(PAGE_SIZE, Some(prot)).unwrap(); if mem1.addr > mem2.addr { bus.add(0x0, mem1).unwrap(); diff --git a/alioth/src/utils/utils.rs b/alioth/src/utils/utils.rs index b4004e1..fc923d6 100644 --- a/alioth/src/utils/utils.rs +++ b/alioth/src/utils/utils.rs @@ -14,6 +14,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; +#[cfg(target_os = "linux")] pub mod ioctls; #[macro_export] diff --git a/alioth/src/virtio/dev/dev.rs b/alioth/src/virtio/dev/dev.rs index 5e1ed3c..fb918b9 100644 --- a/alioth/src/virtio/dev/dev.rs +++ b/alioth/src/virtio/dev/dev.rs @@ -35,9 +35,12 @@ use crate::virtio::{DeviceId, IrqSender, Result, VirtioFeature}; pub mod blk; pub mod entropy; +#[cfg(target_os = "linux")] pub mod fs; +#[cfg(target_os = "linux")] #[path = "net/net.rs"] pub mod net; +#[cfg(target_os = "linux")] #[path = "vsock/vsock.rs"] pub mod vsock; diff --git a/alioth/src/virtio/virtio.rs b/alioth/src/virtio/virtio.rs index 7858d6e..866cf2f 100644 --- a/alioth/src/virtio/virtio.rs +++ b/alioth/src/virtio/virtio.rs @@ -25,8 +25,10 @@ pub mod dev; pub mod pci; #[path = "queue/queue.rs"] pub mod queue; +#[cfg(target_os = "linux")] #[path = "vhost/vhost.rs"] pub mod vhost; +#[cfg(target_os = "linux")] pub mod vu; #[derive(Debug, Error)] @@ -70,6 +72,7 @@ pub enum Error { #[error("vhost-user backend is missing device feature {0:#x}")] VuMissingDeviceFeature(u64), + #[cfg(target_os = "linux")] #[error("vhost-user backend is missing protocol feature {0:x?}")] VuMissingProtocolFeature(vu::VuFeature),