feat: skeleton for running VMs on Apple silicon

Signed-off-by: Changyuan Lyu <changyuanl@google.com>
This commit is contained in:
Changyuan Lyu 2024-06-16 21:51:36 -07:00 committed by Lencerf
parent 7065002d42
commit 78e4f92e96
7 changed files with 257 additions and 14 deletions

View file

@ -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 {

View file

@ -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<KvmError> },
}

200
alioth/src/hv/hvf/hvf.rs Normal file
View file

@ -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<u64> {
unimplemented!()
}
fn run(&mut self, _entry: VmEntry) -> Result<VmExit> {
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<u32> {
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<Self::IrqFd> {
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<Self::IoeventFd> {
unimplemented!()
}
fn deregister(&self, _fd: &Self::IoeventFd) -> Result<()> {
unimplemented!()
}
fn register(
&self,
_fd: &Self::IoeventFd,
_gpa: usize,
_len: u8,
_data: Option<u64>,
) -> 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<Self::IoeventFdRegistry> {
unimplemented!()
}
fn create_msi_sender(&self) -> Result<Self::MsiSender> {
unimplemented!()
}
fn create_vcpu(&self, _id: u32) -> Result<Self::Vcpu> {
unimplemented!()
}
fn create_vm_memory(&mut self) -> Result<Self::Memory> {
unimplemented!()
}
fn stop_vcpu<T>(_id: u32, _handle: &JoinHandle<T>) -> Result<()> {
unimplemented!()
}
}
#[derive(Debug)]
pub struct Hvf {}
impl Hypervisor for Hvf {
type Vm = HvfVm;
fn create_vm(&self, _config: &super::VmConfig) -> Result<Self::Vm> {
unimplemented!()
}
}

View file

@ -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<i32>, name: Option<&CStr>) -> Result<Self> {
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();

View file

@ -14,6 +14,7 @@
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(target_os = "linux")]
pub mod ioctls;
#[macro_export]

View file

@ -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;

View file

@ -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),