From 31258fa8eca0cbdc741167b86a81047c9e4b3001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Fri, 20 Sep 2024 11:34:51 +0200 Subject: [PATCH 1/5] fix: make args `String`s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/bin/uhyve.rs | 8 ++------ src/hypercall.rs | 4 ++-- src/params.rs | 3 +-- src/vm.rs | 5 ++--- uhyve-interface/src/parameters.rs | 2 +- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/bin/uhyve.rs b/src/bin/uhyve.rs index 82c5e438..0bd1868c 100644 --- a/src/bin/uhyve.rs +++ b/src/bin/uhyve.rs @@ -1,9 +1,6 @@ #![warn(rust_2018_idioms)] -use std::{ - ffi::OsString, iter, num::ParseIntError, ops::RangeInclusive, path::PathBuf, process, - str::FromStr, -}; +use std::{iter, num::ParseIntError, ops::RangeInclusive, path::PathBuf, process, str::FromStr}; use clap::{error::ErrorKind, Command, CommandFactory, Parser}; use core_affinity::CoreId; @@ -63,8 +60,7 @@ struct Args { kernel: PathBuf, /// Arguments to forward to the kernel - #[clap(value_parser)] - kernel_args: Vec, + kernel_args: Vec, } #[derive(Parser, Debug)] diff --git a/src/hypercall.rs b/src/hypercall.rs index 1abc741a..a1e0ebee 100644 --- a/src/hypercall.rs +++ b/src/hypercall.rs @@ -1,5 +1,5 @@ use std::{ - ffi::{OsStr, OsString}, + ffi::OsStr, io::{self, Error, ErrorKind, Write}, os::unix::ffi::OsStrExt, }; @@ -172,7 +172,7 @@ pub fn uart_buffer(sysuart: &SerialWriteBufferParams, mem: &MmapMemory) { } /// Copies the arguments of the application into the VM's memory to the destinations specified in `syscmdval`. -pub fn copy_argv(path: &OsStr, argv: &[OsString], syscmdval: &CmdvalParams, mem: &MmapMemory) { +pub fn copy_argv(path: &OsStr, argv: &[String], syscmdval: &CmdvalParams, mem: &MmapMemory) { // copy kernel path as first argument let argvp = mem .host_address(syscmdval.argv) diff --git a/src/params.rs b/src/params.rs index 11d69f64..8793fcf6 100644 --- a/src/params.rs +++ b/src/params.rs @@ -1,5 +1,4 @@ use std::{ - ffi::OsString, fmt, num::{NonZeroU32, ParseIntError, TryFromIntError}, str::FromStr, @@ -35,7 +34,7 @@ pub struct Params { pub gdb_port: Option, /// Arguments to forward to the kernel - pub kernel_args: Vec, + pub kernel_args: Vec, } #[allow(clippy::derivable_impls)] diff --git a/src/vm.rs b/src/vm.rs index a789b89f..9c0fcc56 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,4 @@ use std::{ - ffi::OsString, fmt, fs, io, marker::PhantomData, num::NonZeroU32, @@ -107,7 +106,7 @@ pub struct UhyveVm { pub mem: Arc, num_cpus: u32, path: PathBuf, - args: Vec, + args: Vec, boot_info: *const RawBootInfo, verbose: bool, pub virtio_device: Arc>, @@ -190,7 +189,7 @@ impl UhyveVm { &self.path } - pub fn args(&self) -> &Vec { + pub fn args(&self) -> &Vec { &self.args } diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/parameters.rs index 905bf74c..5b3d13ca 100644 --- a/uhyve-interface/src/parameters.rs +++ b/uhyve-interface/src/parameters.rs @@ -22,7 +22,7 @@ impl CmdsizeParams { /// - `args` is a list of strings that form the parameters. (E.g., `["-v", "myarg"]`) /// /// Note that this hypercall only transfers the sizes. It usually has to be followed up with the [`Cmdval` Hypercall](crate::Hypercall::Cmdval). - pub fn update(&mut self, path: &std::path::Path, args: &[std::ffi::OsString]) { + pub fn update(&mut self, path: &std::path::Path, args: &[String]) { self.argc = 0; self.argsz[0] = path.as_os_str().len() as i32 + 1; From af672cb4ffe2e6f4ebca585baa72f5823515688d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Fri, 20 Sep 2024 13:35:36 +0200 Subject: [PATCH 2/5] feat: create FDT with bootargs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/consts.rs | 1 + src/fdt.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/vm.rs | 34 +++++++++++++++++++-- 6 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 src/fdt.rs diff --git a/Cargo.lock b/Cargo.lock index 740cb55e..9751c94e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ dependencies = [ "tun-tap", "uhyve-interface", "virtio-bindings", + "vm-fdt", "vmm-sys-util", "x86_64", "xhypervisor", @@ -1511,6 +1512,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241" +[[package]] +name = "vm-fdt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" + [[package]] name = "vmm-sys-util" version = "0.12.1" diff --git a/Cargo.toml b/Cargo.toml index 47fd4e4a..2b516922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ virtio-bindings = { version = "0.2", features = ["virtio-v4_14_0"] } rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } sysinfo = { version = "0.31.4", default-features = false, features = ["system"] } +vm-fdt = "0.3" [target.'cfg(target_os = "linux")'.dependencies] kvm-bindings = "0.9" diff --git a/src/consts.rs b/src/consts.rs index b33727f3..da809793 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -13,6 +13,7 @@ pub const BOOT_PML4: GuestPhysAddr = GuestPhysAddr::new(0x10000); pub const BOOT_PGT: GuestPhysAddr = BOOT_PML4; pub const BOOT_PDPTE: GuestPhysAddr = GuestPhysAddr::new(0x11000); pub const BOOT_PDE: GuestPhysAddr = GuestPhysAddr::new(0x12000); +pub const FDT_ADDR: GuestPhysAddr = GuestPhysAddr::new(0x5000); pub const BOOT_INFO_ADDR: GuestPhysAddr = GuestPhysAddr::new(0x9000); pub const EFER_SCE: u64 = 1; /* System Call Extensions */ pub const EFER_LME: u64 = 1 << 8; /* Long mode enable */ diff --git a/src/fdt.rs b/src/fdt.rs new file mode 100644 index 00000000..acdc1a5c --- /dev/null +++ b/src/fdt.rs @@ -0,0 +1,81 @@ +use vm_fdt::{FdtWriter, FdtWriterNode, FdtWriterResult}; + +pub struct Fdt { + writer: FdtWriter, + root_node: FdtWriterNode, + kernel_args: String, + app_args: String, +} + +impl Fdt { + pub fn new() -> FdtWriterResult { + let mut writer = FdtWriter::new()?; + + let root_node = writer.begin_node("")?; + writer.property_string("compatible", "hermit,uhyve")?; + writer.property_u32("#address-cells", 0x2)?; + writer.property_u32("#size-cells", 0x2)?; + + let kernel_args = String::new(); + let app_args = String::new(); + + Ok(Self { + writer, + root_node, + kernel_args, + app_args, + }) + } + + pub fn finish(mut self) -> FdtWriterResult> { + let chosen_node = self.writer.begin_node("chosen")?; + let bootargs = match (self.kernel_args.as_str(), self.app_args.as_str()) { + ("", "") => String::new(), + (_kernel_args, "") => self.kernel_args, + ("", app_args) => format!("-- {app_args}"), + (kernel_args, app_args) => format!("{kernel_args} -- {app_args}"), + }; + self.writer.property_string("bootargs", &bootargs)?; + self.writer.end_node(chosen_node)?; + + self.writer.end_node(self.root_node)?; + + self.writer.finish() + } + + pub fn kernel_arg(mut self, kernel_arg: &str) -> Self { + if !self.kernel_args.is_empty() { + self.kernel_args.push(' '); + } + + self.kernel_args.push_str(kernel_arg); + + self + } + + pub fn kernel_args(mut self, kernel_args: impl IntoIterator>) -> Self { + for arg in kernel_args { + self = self.kernel_arg(arg.as_ref()); + } + + self + } + + pub fn app_arg(mut self, app_arg: &str) -> Self { + if !self.app_args.is_empty() { + self.app_args.push(' '); + } + + self.app_args.push_str(app_arg); + + self + } + + pub fn app_args(mut self, app_args: impl IntoIterator>) -> Self { + for arg in app_args { + self = self.app_arg(arg.as_ref()); + } + + self + } +} diff --git a/src/lib.rs b/src/lib.rs index 147ddd11..c925f26a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ extern crate log; mod arch; pub mod consts; +mod fdt; #[cfg(target_os = "linux")] pub mod linux; #[cfg(target_os = "linux")] diff --git a/src/vm.rs b/src/vm.rs index 9c0fcc56..20032b2e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -23,8 +23,14 @@ use crate::arch::x86_64::{ #[cfg(all(target_arch = "x86_64", target_os = "linux"))] use crate::linux::x86_64::kvm_cpu::initialize_kvm; use crate::{ - arch, arch::FrequencyDetectionFailed, consts::*, mem::MmapMemory, os::HypervisorError, - params::Params, vcpu::VirtualCPU, virtio::*, + arch::{self, FrequencyDetectionFailed}, + consts::*, + fdt::Fdt, + mem::MmapMemory, + os::HypervisorError, + params::Params, + vcpu::VirtualCPU, + virtio::*, }; pub type HypervisorResult = Result; @@ -227,6 +233,28 @@ impl UhyveVm { ); self.entry_point = entry_point; + let sep = self + .args + .iter() + .enumerate() + .find(|(_i, arg)| *arg == "--") + .map(|(i, _arg)| i) + .unwrap_or_else(|| self.args.len()); + + let fdt = Fdt::new() + .unwrap() + .kernel_args(&self.args[..sep]) + .app_args(self.args.get(sep + 1..).unwrap_or_default()) + .finish() + .unwrap(); + + debug!("fdt.len() = {}", fdt.len()); + assert!(fdt.len() < (BOOT_INFO_ADDR - FDT_ADDR) as usize); + unsafe { + let fdt_ptr = self.mem.host_address.add(FDT_ADDR.as_u64() as usize); + fdt_ptr.copy_from_nonoverlapping(fdt.as_ptr(), fdt.len()); + } + let boot_info = BootInfo { hardware_info: HardwareInfo { phys_addr_range: self.mem.guest_address.as_u64() @@ -235,7 +263,7 @@ impl UhyveVm { SerialPortBase::new((uhyve_interface::HypercallAddress::Uart as u16).into()) .unwrap() }), - device_tree: None, + device_tree: Some(FDT_ADDR.as_u64().try_into().unwrap()), }, load_info, platform_info: PlatformInfo::Uhyve { From ec49be9e7299b7f884d2903eec451d9270ed751b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Fri, 20 Sep 2024 13:59:43 +0200 Subject: [PATCH 3/5] feat: put env vars into FDT bootargs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/fdt.rs | 26 ++++++++++++++++++++++++++ src/vm.rs | 3 ++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9751c94e..55c405f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1250,6 +1250,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "simdutf8" version = "0.1.4" @@ -1454,6 +1460,7 @@ dependencies = [ "raw-cpuid", "rftrace", "rftrace-frontend", + "shell-words", "sysinfo", "thiserror", "time", diff --git a/Cargo.toml b/Cargo.toml index 2b516922..1469ad6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ uhyve-interface = { version = "0.1.1", path = "uhyve-interface", features = ["st virtio-bindings = { version = "0.2", features = ["virtio-v4_14_0"] } rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } +shell-words = "1" sysinfo = { version = "0.31.4", default-features = false, features = ["system"] } vm-fdt = "0.3" diff --git a/src/fdt.rs b/src/fdt.rs index acdc1a5c..e9be1e3b 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -1,3 +1,5 @@ +use std::fmt::Write; + use vm_fdt::{FdtWriter, FdtWriterNode, FdtWriterResult}; pub struct Fdt { @@ -61,6 +63,30 @@ impl Fdt { self } + pub fn env(mut self, key: &str, value: &str) -> Self { + if !self.kernel_args.is_empty() { + self.kernel_args.push(' '); + } + + let key = shell_words::quote(key); + let value = shell_words::quote(value); + + write!(&mut self.kernel_args, "env={key}={value}").unwrap(); + + self + } + + pub fn envs( + mut self, + envs: impl IntoIterator, impl AsRef)>, + ) -> Self { + for (key, value) in envs { + self = self.env(key.as_ref(), value.as_ref()); + } + + self + } + pub fn app_arg(mut self, app_arg: &str) -> Self { if !self.app_args.is_empty() { self.app_args.push(' '); diff --git a/src/vm.rs b/src/vm.rs index 20032b2e..ec819573 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,5 @@ use std::{ - fmt, fs, io, + env, fmt, fs, io, marker::PhantomData, num::NonZeroU32, path::PathBuf, @@ -245,6 +245,7 @@ impl UhyveVm { .unwrap() .kernel_args(&self.args[..sep]) .app_args(self.args.get(sep + 1..).unwrap_or_default()) + .envs(env::vars()) .finish() .unwrap(); From 0f1e29b8c064e8c42fcfca924b9d0ed12955b466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 24 Sep 2024 13:10:06 +0200 Subject: [PATCH 4/5] feat: put memory layout into FDT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/fdt.rs | 15 ++++++++++++++- src/vm.rs | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/fdt.rs b/src/fdt.rs index e9be1e3b..ac745f19 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -1,5 +1,6 @@ -use std::fmt::Write; +use std::{fmt::Write, ops::Range}; +use uhyve_interface::GuestPhysAddr; use vm_fdt::{FdtWriter, FdtWriterNode, FdtWriterResult}; pub struct Fdt { @@ -45,6 +46,18 @@ impl Fdt { self.writer.finish() } + pub fn memory(mut self, memory: Range) -> FdtWriterResult { + let node_name = format!("memory@{:x}", memory.start); + let reg = &[memory.start.as_u64(), memory.end - memory.start][..]; + + let memory_node = self.writer.begin_node(&node_name)?; + self.writer.property_string("device_type", "memory")?; + self.writer.property_array_u64("reg", reg)?; + self.writer.end_node(memory_node)?; + + Ok(self) + } + pub fn kernel_arg(mut self, kernel_arg: &str) -> Self { if !self.kernel_args.is_empty() { self.kernel_args.push(' '); diff --git a/src/vm.rs b/src/vm.rs index ec819573..e2d00690 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -242,6 +242,8 @@ impl UhyveVm { .unwrap_or_else(|| self.args.len()); let fdt = Fdt::new() + .unwrap() + .memory(self.mem.guest_address..self.mem.guest_address + self.mem.memory_size as u64) .unwrap() .kernel_args(&self.args[..sep]) .app_args(self.args.get(sep + 1..).unwrap_or_default()) From 53c460831cb880c63262dfba344d38fe31e2f222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 24 Sep 2024 14:55:02 +0200 Subject: [PATCH 5/5] docs(fdt): add documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- src/fdt.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/fdt.rs b/src/fdt.rs index ac745f19..d545c881 100644 --- a/src/fdt.rs +++ b/src/fdt.rs @@ -1,8 +1,11 @@ +//! Flattened Device Trees (FDT). + use std::{fmt::Write, ops::Range}; use uhyve_interface::GuestPhysAddr; use vm_fdt::{FdtWriter, FdtWriterNode, FdtWriterResult}; +/// A builder for an FDT. pub struct Fdt { writer: FdtWriter, root_node: FdtWriterNode, @@ -11,6 +14,7 @@ pub struct Fdt { } impl Fdt { + /// Creates a new FDT builder. pub fn new() -> FdtWriterResult { let mut writer = FdtWriter::new()?; @@ -30,14 +34,17 @@ impl Fdt { }) } + /// Builds and returns the FDT. pub fn finish(mut self) -> FdtWriterResult> { - let chosen_node = self.writer.begin_node("chosen")?; + // The bootargs have the format `[KERNEL_ARGS] -- [APP_ARGS]` let bootargs = match (self.kernel_args.as_str(), self.app_args.as_str()) { ("", "") => String::new(), (_kernel_args, "") => self.kernel_args, ("", app_args) => format!("-- {app_args}"), (kernel_args, app_args) => format!("{kernel_args} -- {app_args}"), }; + + let chosen_node = self.writer.begin_node("chosen")?; self.writer.property_string("bootargs", &bootargs)?; self.writer.end_node(chosen_node)?; @@ -46,6 +53,7 @@ impl Fdt { self.writer.finish() } + /// Adds a `/memory` node to the FDT. pub fn memory(mut self, memory: Range) -> FdtWriterResult { let node_name = format!("memory@{:x}", memory.start); let reg = &[memory.start.as_u64(), memory.end - memory.start][..]; @@ -58,6 +66,7 @@ impl Fdt { Ok(self) } + /// Adds a kernel argument to the FDT. pub fn kernel_arg(mut self, kernel_arg: &str) -> Self { if !self.kernel_args.is_empty() { self.kernel_args.push(' '); @@ -68,6 +77,7 @@ impl Fdt { self } + /// Adds kernel arguments to the FDT. pub fn kernel_args(mut self, kernel_args: impl IntoIterator>) -> Self { for arg in kernel_args { self = self.kernel_arg(arg.as_ref()); @@ -76,6 +86,7 @@ impl Fdt { self } + /// Adds an environment variable to the FDT. pub fn env(mut self, key: &str, value: &str) -> Self { if !self.kernel_args.is_empty() { self.kernel_args.push(' '); @@ -89,6 +100,7 @@ impl Fdt { self } + /// Adds environment variables to the FDT. pub fn envs( mut self, envs: impl IntoIterator, impl AsRef)>, @@ -100,6 +112,7 @@ impl Fdt { self } + /// Adds an app argument to the FDT. pub fn app_arg(mut self, app_arg: &str) -> Self { if !self.app_args.is_empty() { self.app_args.push(' '); @@ -110,6 +123,7 @@ impl Fdt { self } + /// Adds app arguments to the FDT. pub fn app_args(mut self, app_args: impl IntoIterator>) -> Self { for arg in app_args { self = self.app_arg(arg.as_ref());