From 2d5ae0b5744524012dc17b8c60a0b433d14691fd Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Tue, 23 Dec 2025 13:57:06 -0800 Subject: [PATCH] refactor(vm): separate argument parsing and device creation Introduce a unified VM config structure, let CLI focuse on argument parsing only, and move device creation into core crate. Signed-off-by: Changyuan Lyu --- alioth-cli/src/boot.rs | 372 ++++++++++-------------------------- alioth/src/board/board.rs | 1 + alioth/src/hv/hv.rs | 21 ++ alioth/src/lib.rs | 1 + alioth/src/loader/loader.rs | 5 +- alioth/src/vm/config.rs | 118 ++++++++++++ alioth/src/{ => vm}/vm.rs | 122 ++++++++++-- 7 files changed, 358 insertions(+), 282 deletions(-) create mode 100644 alioth/src/vm/config.rs rename alioth/src/{ => vm}/vm.rs (78%) diff --git a/alioth-cli/src/boot.rs b/alioth-cli/src/boot.rs index 8204bb8b..7bb4d3bc 100644 --- a/alioth-cli/src/boot.rs +++ b/alioth-cli/src/boot.rs @@ -14,6 +14,7 @@ use std::collections::HashMap; use std::ffi::CString; +use std::mem; use std::path::{Path, PathBuf}; use alioth::board::{BoardConfig, CpuConfig}; @@ -22,35 +23,21 @@ use alioth::device::fw_cfg::FwCfgItemParam; use alioth::errors::{DebugTrace, trace_error}; #[cfg(target_os = "macos")] use alioth::hv::Hvf; -use alioth::hv::{self, Coco}; #[cfg(target_os = "linux")] -use alioth::hv::{Kvm, KvmConfig}; +use alioth::hv::Kvm; +use alioth::hv::{Coco, HvConfig}; use alioth::loader::{Executable, Payload}; use alioth::mem::{MemBackend, MemConfig}; #[cfg(target_os = "linux")] use alioth::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam}; -#[cfg(target_os = "linux")] -use alioth::virtio::DeviceId; use alioth::virtio::dev::balloon::BalloonParam; use alioth::virtio::dev::blk::BlkFileParam; use alioth::virtio::dev::entropy::EntropyParam; -use alioth::virtio::dev::fs::shared_dir::SharedDirParam; -#[cfg(target_os = "linux")] -use alioth::virtio::dev::fs::vu::VuFsParam; -#[cfg(target_os = "linux")] -use alioth::virtio::dev::net::tap::NetTapParam; -#[cfg(target_os = "macos")] -use alioth::virtio::dev::net::vmnet::NetVmnetParam; -use alioth::virtio::dev::vsock::UdsVsockParam; -#[cfg(target_os = "linux")] -use alioth::virtio::dev::vsock::VhostVsockParam; -#[cfg(target_os = "linux")] -use alioth::virtio::vu::frontend::VuFrontendParam; use alioth::virtio::worker::WorkerApi; use alioth::vm::Machine; +use alioth::vm::config::{BlkParam, Config, FsParam, NetParam, VsockParam}; use clap::Args; -use serde::Deserialize; -use serde_aco::{Help, help_text}; +use serde_aco::help_text; use snafu::{ResultExt, Snafu}; use crate::objects::{DOC_OBJECTS, parse_objects}; @@ -71,95 +58,17 @@ pub enum Error { Hypervisor { source: alioth::hv::Error }, #[snafu(display("Failed to create a VM"))] CreateVm { source: alioth::vm::Error }, - #[snafu(display("Failed to create a device"))] - CreateDevice { source: alioth::vm::Error }, #[snafu(display("Failed to boot a VM"))] BootVm { source: alioth::vm::Error }, #[snafu(display("VM did not shutdown peacefully"))] WaitVm { source: alioth::vm::Error }, } -#[derive(Debug, Deserialize, Clone, Help)] -#[cfg_attr(target_os = "macos", derive(Default))] -enum Hypervisor { - /// KVM backed by the Linux kernel. - #[cfg(target_os = "linux")] - #[serde(alias = "kvm")] - Kvm(KvmConfig), - /// macOS Hypervisor Framework. - #[cfg(target_os = "macos")] - #[serde(alias = "hvf")] - #[default] - Hvf, -} - -#[cfg(target_os = "linux")] -impl Default for Hypervisor { - fn default() -> Self { - Hypervisor::Kvm(KvmConfig::default()) - } -} - -#[derive(Debug, Deserialize, Clone, Help)] -enum FsParam { - /// VirtIO FS device backed by a shared directory. - #[serde(alias = "dir")] - Dir(SharedDirParam), - #[cfg(target_os = "linux")] - /// VirtIO FS device backed by a vhost-user process, e.g. virtiofsd. - #[serde(alias = "vu")] - Vu(VuFsParam), -} - -#[derive(Debug, Deserialize, Clone, Help)] -enum VsockParam { - #[cfg(target_os = "linux")] - /// Vsock device backed by host kernel vhost-vsock module. - #[serde(alias = "vhost")] - Vhost(VhostVsockParam), - /// Vsock device mapped to a Unix domain socket. - #[serde(alias = "uds")] - Uds(UdsVsockParam), -} - -#[cfg(target_os = "linux")] -#[derive(Deserialize, Help)] -struct VuSocket { - socket: Box, -} - -#[derive(Deserialize, Help)] -enum NetParam { - /// VirtIO net device backed by TUN/TAP, MacVTap, or IPVTap. - #[cfg(target_os = "linux")] - #[serde(alias = "tap")] - Tap(NetTapParam), - /// VirtIO net device backed by vmnet framework. - #[cfg(target_os = "macos")] - #[serde(alias = "vmnet")] - Vmnet(NetVmnetParam), - /// vhost-user net device over a Unix domain socket. - #[cfg(target_os = "linux")] - #[serde(alias = "vu")] - Vu(VuSocket), -} - -#[derive(Deserialize, Help)] -enum BlkParam { - /// VirtIO block device backed a disk image file. - #[serde(alias = "file")] - File(BlkFileParam), - #[cfg(target_os = "linux")] - #[serde(alias = "vu")] - /// vhost-user block device over a Unix domain socket. - Vu(VuSocket), -} - #[derive(Args, Debug, Clone)] #[command(arg_required_else_help = true, alias("run"))] pub struct BootArgs { #[arg(long, help( - help_text::("Specify the Hypervisor to run on.") + help_text::("Specify the Hypervisor to run on.") ), value_name = "HV")] hypervisor: Option, @@ -207,10 +116,10 @@ pub struct BootArgs { pvpanic: bool, #[cfg(target_arch = "x86_64")] - #[arg(long = "fw-cfg", help( + #[arg(long, help( help_text::("Add an extra item to the fw_cfg device.") ), value_name = "ITEM")] - fw_cfgs: Vec, + fw_cfg: Vec, /// Add a VirtIO entropy device. #[arg(long)] @@ -269,104 +178,15 @@ pub struct BootArgs { objects: Vec, } -fn add_net( - vm: &Machine, - args: Vec, - objects: &HashMap<&str, &str>, -) -> Result<(), Error> -where - H: hv::Hypervisor + 'static, -{ - for (index, arg) in args.into_iter().enumerate() { - #[cfg(target_os = "linux")] - let param: NetParam = match serde_aco::from_args(&arg, objects) { - Ok(p) => p, - Err(_) => { - let tap_param = serde_aco::from_args::(&arg, objects) - .context(error::ParseArg { arg })?; - NetParam::Tap(tap_param) - } - }; - #[cfg(target_os = "macos")] - let param: NetParam = - serde_aco::from_args(&arg, objects).context(error::ParseArg { arg })?; - match param { - #[cfg(target_os = "linux")] - NetParam::Tap(tap_param) => vm.add_virtio_dev(format!("virtio-net-{index}"), tap_param), - #[cfg(target_os = "linux")] - NetParam::Vu(sock) => { - let param = VuFrontendParam { - id: DeviceId::Net, - socket: sock.socket, - }; - vm.add_virtio_dev(format!("vu-net-{index}"), param) - } - #[cfg(target_os = "macos")] - NetParam::Vmnet(p) => vm.add_virtio_dev(format!("virtio-net-{index}"), p), - } - .context(error::CreateDevice)?; - } - Ok(()) -} - -fn add_blk( - vm: &Machine, - args: Vec, - objects: &HashMap<&str, &str>, -) -> Result<(), Error> -where - H: hv::Hypervisor + 'static, -{ - for (index, opt) in args.into_iter().enumerate() { - let param: BlkParam = match serde_aco::from_args(&opt, objects) { - Ok(param) => param, - Err(_) => match serde_aco::from_args(&opt, objects) { - Ok(param) => BlkParam::File(param), - Err(_) => { - eprintln!("Please update the cmd line to --blk file,path={opt}"); - BlkParam::File(BlkFileParam { - path: PathBuf::from(opt).into(), - readonly: false, - api: WorkerApi::Mio, - }) - } - }, - }; - match param { - BlkParam::File(p) => vm.add_virtio_dev(format!("virtio-blk-{index}"), p), - #[cfg(target_os = "linux")] - BlkParam::Vu(s) => { - let p = VuFrontendParam { - id: DeviceId::Block, - socket: s.socket, - }; - vm.add_virtio_dev(format!("vu-net-{index}"), p) - } - } - .context(error::CreateDevice)?; - } - Ok(()) -} +fn parse_config(args: BootArgs, objects: HashMap<&str, &str>) -> Result { + let mut board_config = BoardConfig::default(); -pub fn boot(args: BootArgs) -> Result<(), Error> { - let objects = parse_objects(&args.objects)?; - let hv_config = if let Some(hv_cfg_opt) = args.hypervisor { - serde_aco::from_args(&hv_cfg_opt, &objects).context(error::ParseArg { arg: hv_cfg_opt })? - } else { - Hypervisor::default() + if let Some(arg) = args.coco { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + board_config.coco = Some(param); }; - let hypervisor = match hv_config { - #[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_args(&c, &objects).context(error::ParseArg { arg: c })?), - }; - let mem_config = if let Some(s) = args.memory { - serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s })? + board_config.mem = if let Some(arg) = args.memory { + serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })? } else { #[cfg(target_os = "linux")] eprintln!( @@ -384,8 +204,8 @@ pub fn boot(args: BootArgs) -> Result<(), Error> { ..Default::default() } }; - let cpu_config = if let Some(s) = args.cpu { - serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s })? + board_config.cpu = if let Some(arg) = args.cpu { + serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })? } else { eprintln!("Please update the cmd line to --cpu count={}", args.num_cpu); CpuConfig { @@ -393,96 +213,87 @@ pub fn boot(args: BootArgs) -> Result<(), Error> { ..Default::default() } }; - let board_config = BoardConfig { - mem: mem_config, - cpu: cpu_config, - coco, + + let mut config = Config { + board: board_config, + pvpanic: args.pvpanic, + ..Default::default() }; - let vm = Machine::new(hypervisor, board_config).context(error::CreateVm)?; - #[cfg(target_arch = "x86_64")] - vm.add_com1().context(error::CreateDevice)?; - #[cfg(target_arch = "aarch64")] - vm.add_pl011().context(error::CreateDevice)?; - #[cfg(target_arch = "aarch64")] - vm.add_pl031(); - - if args.pvpanic { - vm.add_pvpanic().context(error::CreateDevice)?; - } #[cfg(target_arch = "x86_64")] - if args.firmware.is_some() || !args.fw_cfgs.is_empty() { - let params = args - .fw_cfgs - .into_iter() - .map(|s| serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s })) - .collect::, _>>()?; - vm.add_fw_cfg(params.into_iter()) - .context(error::CreateDevice)?; - }; + for arg in args.fw_cfg { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.fw_cfg.push(param); + } if args.entropy { - vm.add_virtio_dev("virtio-entropy", EntropyParam::default()) - .context(error::CreateDevice)?; + config.entropy = Some(EntropyParam::default()); } - add_net(&vm, args.net, &objects)?; - add_blk(&vm, args.blk, &objects)?; - for (index, fs) in args.fs.into_iter().enumerate() { - let param: FsParam = - serde_aco::from_args(&fs, &objects).context(error::ParseArg { arg: fs })?; - match param { - FsParam::Dir(p) => vm.add_virtio_dev(format!("virtio-fs-{index}"), p), - #[cfg(target_os = "linux")] - FsParam::Vu(p) => vm.add_virtio_dev(format!("vu-fs-{index}"), p), + + for arg in args.net { + #[cfg(target_os = "linux")] + let param = if let Ok(param) = serde_aco::from_args(&arg, &objects) { + param + } else { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + NetParam::Tap(param) + }; + #[cfg(target_os = "macos")] + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + + config.net.push(param); + } + + for arg in args.blk { + if let Ok(param) = serde_aco::from_args(&arg, &objects) { + config.blk.push(param); + } else if let Ok(param) = serde_aco::from_args(&arg, &objects) { + config.blk.push(BlkParam::File(param)); + } else { + eprintln!("Please update the cmd line to --blk file,path={arg}"); + config.blk.push(BlkParam::File(BlkFileParam { + path: PathBuf::from(arg).into(), + readonly: false, + api: WorkerApi::Mio, + })); } - .context(error::CreateDevice)?; } - if let Some(vsock) = args.vsock { - let param = - serde_aco::from_args(&vsock, &objects).context(error::ParseArg { arg: vsock })?; - match param { - #[cfg(target_os = "linux")] - VsockParam::Vhost(p) => vm - .add_virtio_dev("vhost-vsock", p) - .context(error::CreateDevice)?, - VsockParam::Uds(p) => vm - .add_virtio_dev("uds-vsock", p) - .context(error::CreateDevice)?, - }; + + for arg in args.fs { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.fs.push(param); } - if let Some(balloon) = args.balloon { - let param: BalloonParam = - serde_aco::from_args(&balloon, &objects).context(error::ParseArg { arg: balloon })?; - vm.add_virtio_dev("virtio-balloon", param) - .context(error::CreateDevice)?; + + if let Some(arg) = args.vsock { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.vsock = Some(param); + } + + if let Some(arg) = args.balloon { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.balloon = Some(param); } #[cfg(target_os = "linux")] - for ioas in args.vfio_ioas.into_iter() { - let param: IoasParam = - serde_aco::from_args(&ioas, &objects).context(error::ParseArg { arg: ioas })?; - vm.add_vfio_ioas(param).context(error::CreateDevice)?; + for arg in args.vfio_ioas { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.vfio_ioas.push(param); } #[cfg(target_os = "linux")] - for (index, vfio) in args.vfio_cdev.into_iter().enumerate() { - let param: CdevParam = - serde_aco::from_args(&vfio, &objects).context(error::ParseArg { arg: vfio })?; - vm.add_vfio_cdev(format!("vfio-{index}").into(), param) - .context(error::CreateDevice)?; + for arg in args.vfio_cdev { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.vfio_cdev.push(param); } #[cfg(target_os = "linux")] - for container in args.vfio_container.into_iter() { - let param: ContainerParam = serde_aco::from_args(&container, &objects) - .context(error::ParseArg { arg: container })?; - vm.add_vfio_container(param).context(error::CreateDevice)?; + for arg in args.vfio_container { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.vfio_container.push(param); } #[cfg(target_os = "linux")] - for (index, group) in args.vfio_group.into_iter().enumerate() { - let param: GroupParam = - serde_aco::from_args(&group, &objects).context(error::ParseArg { arg: group })?; - vm.add_vfio_devs_in_group(&index.to_string(), param) - .context(error::CreateDevice)?; + for arg in args.vfio_group { + let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?; + config.vfio_group.push(param); } let mut payload = Payload { @@ -496,7 +307,30 @@ pub fn boot(args: BootArgs) -> Result<(), Error> { if payload.executable.is_none() { payload.executable = args.pvh.map(Executable::Pvh); } - vm.add_payload(payload); + config.payload = payload; + + Ok(config) +} + +pub fn boot(mut args: BootArgs) -> Result<(), Error> { + let object_args = mem::take(&mut args.objects); + let objects = parse_objects(&object_args)?; + + let hv_config = if let Some(arg) = args.hypervisor.take() { + serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })? + } else { + HvConfig::default() + }; + let hypervisor = match hv_config { + #[cfg(target_os = "linux")] + HvConfig::Kvm(kvm_config) => Kvm::new(kvm_config).context(error::Hypervisor)?, + #[cfg(target_os = "macos")] + HvConfig::Hvf => Hvf {}, + }; + + let config = parse_config(args, objects)?; + + let vm = Machine::new(hypervisor, config).context(error::CreateVm)?; vm.boot().context(error::BootVm)?; vm.wait().context(error::WaitVm)?; diff --git a/alioth/src/board/board.rs b/alioth/src/board/board.rs index bea1809b..dd9ba856 100644 --- a/alioth/src/board/board.rs +++ b/alioth/src/board/board.rs @@ -175,6 +175,7 @@ struct MpSync { pub const PCIE_MMIO_64_SIZE: u64 = 1 << 40; +#[derive(Debug, Deserialize, Default)] pub struct BoardConfig { pub mem: MemConfig, pub cpu: CpuConfig, diff --git a/alioth/src/hv/hv.rs b/alioth/src/hv/hv.rs index 695bebd1..d3e29ce0 100644 --- a/alioth/src/hv/hv.rs +++ b/alioth/src/hv/hv.rs @@ -107,6 +107,27 @@ pub enum Error { pub type Result = std::result::Result; +#[derive(Debug, Deserialize, Clone, Help)] +#[cfg_attr(target_os = "macos", derive(Default))] +pub enum HvConfig { + /// KVM backed by the Linux kernel. + #[cfg(target_os = "linux")] + #[serde(alias = "kvm")] + Kvm(KvmConfig), + /// macOS Hypervisor Framework. + #[cfg(target_os = "macos")] + #[default] + #[serde(alias = "hvf")] + Hvf, +} + +#[cfg(target_os = "linux")] +impl Default for HvConfig { + fn default() -> Self { + HvConfig::Kvm(KvmConfig::default()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MemMapOption { pub read: bool, diff --git a/alioth/src/lib.rs b/alioth/src/lib.rs index 7487999d..46a7fdb8 100644 --- a/alioth/src/lib.rs +++ b/alioth/src/lib.rs @@ -42,6 +42,7 @@ pub(crate) mod utils; pub mod vfio; #[path = "virtio/virtio.rs"] pub mod virtio; +#[path = "vm/vm.rs"] pub mod vm; #[cfg(test)] diff --git a/alioth/src/loader/loader.rs b/alioth/src/loader/loader.rs index dee19d52..1f7e4abe 100644 --- a/alioth/src/loader/loader.rs +++ b/alioth/src/loader/loader.rs @@ -25,6 +25,7 @@ use std::ffi::CString; use std::ops::Range; use std::path::Path; +use serde::Deserialize; use snafu::Snafu; #[cfg(target_arch = "x86_64")] @@ -33,7 +34,7 @@ use crate::arch::reg::{Reg, SReg}; use crate::errors::{DebugTrace, trace_error}; use crate::mem::{MemRegionEntry, MemRegionType}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Deserialize)] pub struct Payload { pub firmware: Option>, pub executable: Option, @@ -41,7 +42,7 @@ pub struct Payload { pub cmdline: Option, } -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub enum Executable { Linux(Box), #[cfg(target_arch = "x86_64")] diff --git a/alioth/src/vm/config.rs b/alioth/src/vm/config.rs new file mode 100644 index 00000000..9e630675 --- /dev/null +++ b/alioth/src/vm/config.rs @@ -0,0 +1,118 @@ +// Copyright 2025 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. + +#[cfg(target_os = "linux")] +use std::path::Path; + +use serde::Deserialize; +use serde_aco::Help; + +use crate::board::BoardConfig; +#[cfg(target_arch = "x86_64")] +use crate::device::fw_cfg::FwCfgItemParam; +use crate::loader::Payload; +use crate::virtio::dev::balloon::BalloonParam; +use crate::virtio::dev::blk::BlkFileParam; +use crate::virtio::dev::entropy::EntropyParam; +use crate::virtio::dev::fs::shared_dir::SharedDirParam; +#[cfg(target_os = "macos")] +use crate::virtio::dev::net::vmnet::NetVmnetParam; +use crate::virtio::dev::vsock::UdsVsockParam; +#[cfg(target_os = "linux")] +use crate::{ + vfio::{CdevParam, ContainerParam, GroupParam, IoasParam}, + virtio::dev::{fs::vu::VuFsParam, net::tap::NetTapParam, vsock::VhostVsockParam}, +}; + +#[cfg(target_os = "linux")] +#[derive(Debug, Deserialize, Help)] +pub struct VuSocket { + pub socket: Box, +} + +#[derive(Debug, Deserialize, Help)] +pub enum NetParam { + /// VirtIO net device backed by TUN/TAP, MacVTap, or IPVTap. + #[cfg(target_os = "linux")] + #[serde(alias = "tap")] + Tap(NetTapParam), + /// VirtIO net device backed by vmnet framework. + #[cfg(target_os = "macos")] + #[serde(alias = "vmnet")] + Vmnet(NetVmnetParam), + /// vhost-user net device over a Unix domain socket. + #[cfg(target_os = "linux")] + #[serde(alias = "vu")] + Vu(VuSocket), +} + +#[derive(Debug, Deserialize, Help)] +pub enum BlkParam { + /// VirtIO block device backed a disk image file. + #[serde(alias = "file")] + File(BlkFileParam), + #[cfg(target_os = "linux")] + #[serde(alias = "vu")] + /// vhost-user block device over a Unix domain socket. + Vu(VuSocket), +} + +#[derive(Debug, Deserialize, Clone, Help)] +pub enum FsParam { + /// VirtIO FS device backed by a shared directory. + #[serde(alias = "dir")] + Dir(SharedDirParam), + #[cfg(target_os = "linux")] + /// VirtIO FS device backed by a vhost-user process, e.g. virtiofsd. + #[serde(alias = "vu")] + Vu(VuFsParam), +} + +#[derive(Debug, Deserialize, Clone, Help)] +pub enum VsockParam { + #[cfg(target_os = "linux")] + /// Vsock device backed by host kernel vhost-vsock module. + #[serde(alias = "vhost")] + Vhost(VhostVsockParam), + /// Vsock device mapped to a Unix domain socket. + #[serde(alias = "uds")] + Uds(UdsVsockParam), +} + +#[derive(Debug, Default, Deserialize)] +pub struct Config { + pub board: BoardConfig, + + pub payload: Payload, + + pub net: Vec, + pub blk: Vec, + pub fs: Vec, + pub vsock: Option, + pub entropy: Option, + pub balloon: Option, + pub pvpanic: bool, + + #[cfg(target_arch = "x86_64")] + pub fw_cfg: Vec, + + #[cfg(target_os = "linux")] + pub vfio_cdev: Vec, + #[cfg(target_os = "linux")] + pub vfio_ioas: Vec, + #[cfg(target_os = "linux")] + pub vfio_group: Vec, + #[cfg(target_os = "linux")] + pub vfio_container: Vec, +} diff --git a/alioth/src/vm.rs b/alioth/src/vm/vm.rs similarity index 78% rename from alioth/src/vm.rs rename to alioth/src/vm/vm.rs index af374795..e787bbe5 100644 --- a/alioth/src/vm.rs +++ b/alioth/src/vm/vm.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod config; + #[cfg(target_os = "linux")] use std::path::Path; use std::sync::Arc; @@ -27,7 +29,7 @@ use snafu::{ResultExt, Snafu}; use crate::arch::layout::{PL011_START, PL031_START}; #[cfg(target_arch = "x86_64")] use crate::arch::layout::{PORT_COM1, PORT_FW_CFG_SELECTOR}; -use crate::board::{ArchBoard, Board, BoardConfig}; +use crate::board::{ArchBoard, Board}; #[cfg(target_arch = "x86_64")] use crate::device::fw_cfg::{FwCfg, FwCfgItemParam}; #[cfg(target_arch = "aarch64")] @@ -38,8 +40,6 @@ use crate::device::pvpanic::PvPanic; #[cfg(target_arch = "x86_64")] use crate::device::serial::Serial; use crate::errors::{DebugTrace, trace_error}; -#[cfg(target_os = "linux")] -use crate::hv::Kvm; use crate::hv::{Hypervisor, IoeventFdRegistry, Vm, VmConfig}; use crate::loader::Payload; use crate::mem::Memory; @@ -62,8 +62,14 @@ use crate::vfio::iommu::{Ioas, Iommu}; use crate::vfio::pci::VfioPciDev; #[cfg(target_os = "linux")] use crate::vfio::{CdevParam, ContainerParam, GroupParam, IoasParam}; +#[cfg(target_os = "linux")] +use crate::virtio::DeviceId; use crate::virtio::dev::{DevParam, Virtio, VirtioDevice}; use crate::virtio::pci::VirtioPciDevice; +#[cfg(target_os = "linux")] +use crate::virtio::vu::frontend::VuFrontendParam; + +use self::config::{BlkParam, Config, FsParam, NetParam, VsockParam}; #[trace_error] #[derive(Snafu, DebugTrace)] @@ -123,18 +129,19 @@ impl Machine where H: Hypervisor + 'static, { - pub fn new(hv: H, mut config: BoardConfig) -> Result { - config.config_fixup()?; + pub fn new(hv: H, config: Config) -> Result { + let mut board_config = config.board; + board_config.config_fixup()?; let vm_config = VmConfig { - coco: config.coco.clone(), + coco: board_config.coco.clone(), }; let mut vm = hv.create_vm(&vm_config)?; let vm_memory = vm.create_vm_memory()?; let memory = Memory::new(vm_memory); - let arch = ArchBoard::new(&hv, &vm, &config)?; + let arch = ArchBoard::new(&hv, &vm, &board_config)?; - let board = Arc::new(Board::new(vm, memory, arch, config)); + let board = Arc::new(Board::new(vm, memory, arch, board_config)); let (event_tx, event_rx) = mpsc::channel(); @@ -156,7 +163,7 @@ where board.arch_init()?; - let machine = Machine { + let vm = Machine { board, event_rx, _event_tx: event_tx, @@ -164,7 +171,97 @@ where iommu: Mutex::new(None), }; - Ok(machine) + #[cfg(target_arch = "x86_64")] + vm.add_com1()?; + #[cfg(target_arch = "aarch64")] + vm.add_pl011()?; + #[cfg(target_arch = "aarch64")] + vm.add_pl031(); + + if config.pvpanic { + vm.add_pvpanic()?; + } + + #[cfg(target_arch = "x86_64")] + if config.payload.firmware.is_some() || !config.fw_cfg.is_empty() { + vm.add_fw_cfg(config.fw_cfg.into_iter())?; + }; + + if let Some(param) = config.entropy { + vm.add_virtio_dev("virtio-entropy", param)?; + } + + for (index, param) in config.net.into_iter().enumerate() { + match param { + #[cfg(target_os = "linux")] + NetParam::Tap(tap_param) => { + vm.add_virtio_dev(format!("virtio-net-{index}"), tap_param) + } + #[cfg(target_os = "linux")] + NetParam::Vu(sock) => { + let param = VuFrontendParam { + id: DeviceId::Net, + socket: sock.socket, + }; + vm.add_virtio_dev(format!("vu-net-{index}"), param) + } + #[cfg(target_os = "macos")] + NetParam::Vmnet(p) => vm.add_virtio_dev(format!("virtio-net-{index}"), p), + }?; + } + for (index, param) in config.blk.into_iter().enumerate() { + match param { + BlkParam::File(p) => vm.add_virtio_dev(format!("virtio-blk-{index}"), p), + #[cfg(target_os = "linux")] + BlkParam::Vu(s) => { + let p = VuFrontendParam { + id: DeviceId::Block, + socket: s.socket, + }; + vm.add_virtio_dev(format!("vu-net-{index}"), p) + } + }?; + } + for (index, param) in config.fs.into_iter().enumerate() { + match param { + FsParam::Dir(p) => vm.add_virtio_dev(format!("virtio-fs-{index}"), p), + #[cfg(target_os = "linux")] + FsParam::Vu(p) => vm.add_virtio_dev(format!("vu-fs-{index}"), p), + }?; + } + + if let Some(param) = config.vsock { + match param { + #[cfg(target_os = "linux")] + VsockParam::Vhost(p) => vm.add_virtio_dev("vhost-vsock", p), + VsockParam::Uds(p) => vm.add_virtio_dev("uds-vsock", p), + }?; + } + if let Some(param) = config.balloon { + vm.add_virtio_dev("virtio-balloon", param)?; + } + + #[cfg(target_os = "linux")] + for param in config.vfio_ioas.into_iter() { + vm.add_vfio_ioas(param)?; + } + #[cfg(target_os = "linux")] + for (index, param) in config.vfio_cdev.into_iter().enumerate() { + vm.add_vfio_cdev(format!("vfio-{index}").into(), param)?; + } + + #[cfg(target_os = "linux")] + for param in config.vfio_container.into_iter() { + vm.add_vfio_container(param)?; + } + #[cfg(target_os = "linux")] + for (index, param) in config.vfio_group.into_iter().enumerate() { + vm.add_vfio_devs_in_group(&index.to_string(), param)?; + } + + vm.add_payload(config.payload); + + Ok(vm) } #[cfg(target_arch = "x86_64")] @@ -309,7 +406,10 @@ where } #[cfg(target_os = "linux")] -impl Machine { +impl Machine +where + H: Hypervisor + 'static, +{ const DEFAULT_NAME: &str = "default"; pub fn add_vfio_ioas(&self, param: IoasParam) -> Result, Error> {