diff --git a/Cargo.lock b/Cargo.lock index 564b49e..543fb4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,21 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bstr" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +dependencies = [ + "memchr", +] + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + [[package]] name = "cfg-if" version = "1.0.0" @@ -55,6 +70,15 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "erased-serde" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0465971a8cc1fa2455c8465aaa377131e1f1cf4983280f474a13e68793aa770c" +dependencies = [ + "serde 1.0.125", +] + [[package]] name = "itoa" version = "0.4.7" @@ -98,6 +122,21 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mlua" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2fc8e1085d53b72898c59ceee1980b5826b0c98ce99886b7518f0ead00e5cb" +dependencies = [ + "bstr", + "cc", + "erased-serde", + "lazy_static", + "num-traits 0.2.14", + "pkg-config", + "serde 1.0.125", +] + [[package]] name = "nom" version = "5.1.2" @@ -127,6 +166,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "proc-macro2" version = "1.0.26" @@ -276,7 +321,9 @@ dependencies = [ "anyhow", "config", "kiam", + "mlua", "serde 1.0.125", + "toml", ] [[package]] diff --git a/config/global.toml b/config/global.toml new file mode 100644 index 0000000..7287f94 --- /dev/null +++ b/config/global.toml @@ -0,0 +1,23 @@ +[qemu] +script = "qemu.lua" + +default = [ + "-rtc", "driftfix=slew", + "-serial", "stdio", + "-no-hpet", + "-boot", "strict=on" +] + +arch.i686 = [] +arch.x86_64 = ["-global", "kvm-pit.lost_tick_policy=discard"] + +uefi = [ + # OVMF will hang if S3 is not disabled + # disable S4 too, since libvirt does that 🤷 + # https://bugs.archlinux.org/task/59465#comment172528 + "-global", "ICH9-LPC.disable_s3=1", + "-global", "ICH9-LPC.disable_s4=1" +] + +[[disk.default]] +flag = "blockdev" diff --git a/config/qemu.lua b/config/qemu.lua new file mode 100644 index 0000000..1d09c16 --- /dev/null +++ b/config/qemu.lua @@ -0,0 +1,37 @@ +function build_command(instance, args) + args:add("-rtc", "driftfix=slew") + args:add("-serial", "stdio") + args:add("-no-hpet") + args:add("-boot", "strict=on") + + if instance.kvm then + args:add("-enable-kvm") + end + + if instance.arch == "x86_64" or instance.arch == "i868" then + args:add("-global", "kvm-pit.lost_tick_policy=discard") + end + + args:add("-no-user-config") + args:add("-no-defaults") + args:add("-no-shutdown") + args:add("-m", tostring(instance.memory)) + + local cpu = instance.cpu; + args:add(string.format("%d,sockets=%d,dies=%d,cores=%d,threads=%d", + cpu.amount, + cpu.sockets, + cpu.dies, + cpu.cores, + cpu.threads)) + + if instance.uefi.enabled and string.find(instance.chipset, "q35") == 0 then + -- OVMF will hang if S3 is not disabled + -- disable S4 too, since libvirt does that 🤷 + -- https://bugs.archlinux.org/task/59465#comment172528 + args:add("-global", "ICH9-LPC.disable_s3=1") + args:add("-global", "ICH9-LPC.disable_s4=1") + end + + return args +end \ No newline at end of file diff --git a/vore-core/Cargo.toml b/vore-core/Cargo.toml index b7f3e35..44c91d2 100644 --- a/vore-core/Cargo.toml +++ b/vore-core/Cargo.toml @@ -9,5 +9,7 @@ edition = "2018" [dependencies] config = "0.11.0" serde = { version = "1.0.125", features = ["serde_derive"] } +toml = "*" anyhow = "1.0.40" -kiam = "0.1" \ No newline at end of file +kiam = "0.1" +mlua = { version = "0.5.3", features = ["lua54", "serialize"] } \ No newline at end of file diff --git a/vore-core/src/global_config.rs b/vore-core/src/global_config.rs new file mode 100644 index 0000000..8ba6bfb --- /dev/null +++ b/vore-core/src/global_config.rs @@ -0,0 +1,21 @@ +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GlobalConfig { + pub qemu: GlobalQemuConfig, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GlobalQemuConfig { + pub default: Vec, + pub arch: HashMap>, + pub uefi: Vec, +} + +impl GlobalConfig { + pub fn load(toml: &str) -> Result { + toml::from_str(toml).context("Failed to parse toml for global config") + } +} diff --git a/vore-core/src/instance_config.rs b/vore-core/src/instance_config.rs index f57ed1d..75dd333 100644 --- a/vore-core/src/instance_config.rs +++ b/vore-core/src/instance_config.rs @@ -7,6 +7,8 @@ use std::str::FromStr; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct InstanceConfig { pub name: String, + pub arch: String, + pub chipset: String, pub kvm: bool, pub memory: u64, pub cpu: CpuConfig, @@ -61,6 +63,8 @@ impl Default for InstanceConfig { fn default() -> Self { InstanceConfig { name: "vore".to_string(), + arch: std::env::consts::ARCH.to_string(), + chipset: "q35".to_string(), kvm: true, // 2 GB memory: 2 * 1024 * 1024 * 1024, diff --git a/vore-core/src/lib.rs b/vore-core/src/lib.rs index 292c7d2..c24fbc7 100644 --- a/vore-core/src/lib.rs +++ b/vore-core/src/lib.rs @@ -1,3 +1,7 @@ +mod global_config; mod instance_config; +mod qemu; +pub use global_config::*; pub use instance_config::*; +pub use qemu::build_qemu_command; diff --git a/vore-core/src/qemu.rs b/vore-core/src/qemu.rs new file mode 100644 index 0000000..60b28ea --- /dev/null +++ b/vore-core/src/qemu.rs @@ -0,0 +1,47 @@ +use crate::{GlobalConfig, InstanceConfig}; +use mlua::{Function, LuaSerdeExt, MultiValue, ToLua, UserData, UserDataMethods, Value}; +use serde::Deserialize; + +#[derive(Debug, Default, Deserialize, Clone)] +struct LuaFreeList(Vec); + +impl UserData for LuaFreeList { + fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method_mut("add", |_, this, args: MultiValue| { + for item in args.iter() { + if let Value::String(item) = item { + this.0.push(item.to_str()?.to_string()) + } + } + + Ok(Value::Nil) + }) + } +} + +pub fn build_qemu_command(config: &InstanceConfig, global_config: &GlobalConfig) -> Vec { + let lua = mlua::Lua::new(); + // TODO: load correct script + lua.load(include_str!("../../config/qemu.lua")) + .eval::<()>() + .unwrap(); + let val: Function = lua.globals().get("build_command").unwrap(); + let item = LuaFreeList::default(); + let multi = MultiValue::from_vec(vec![ + lua.to_value(config).unwrap(), + item.to_lua(&lua).unwrap(), + ]); + let mut x = val.call::(multi).unwrap(); + println!("{:?}", x); + + let mut cmd: Vec = vec![]; + cmd.push("-name".to_string()); + cmd.push(format!("guest={},debug-threads=on", config.name)); + + cmd.push("-S".to_string()); + cmd.push("-msg".to_string()); + cmd.push("timestamps=on".to_string()); + cmd.append(&mut x.0); + + cmd +} diff --git a/vored/src/instance.rs b/vored/src/instance.rs index 1ca6ba6..20ee63b 100644 --- a/vored/src/instance.rs +++ b/vored/src/instance.rs @@ -1,4 +1,3 @@ -use std::fmt::{Display, Formatter}; use std::process::Child; use vore_core::InstanceConfig; @@ -18,123 +17,6 @@ impl Instance { } } -#[derive(Debug, Clone)] -pub struct ArgumentList { - items: Vec, -} - -impl Display for ArgumentList { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let item = self.items.iter().fold(String::new(), |mut x, item| { - if x.len() > 0 { - x.push_str(" ") - } - x.push_str(item.as_str()); - x - }); - f.write_str(&item) - } -} - -impl ArgumentList { - pub fn new(command: &'static str) -> ArgumentList { - ArgumentList { - items: vec![Argument::Borrowed(command)], - } - } - - pub fn pair(&mut self, key: &'static str, value: String) { - self.push(key); - self.push(value); - } -} - -pub trait PushArgument { - fn push(&mut self, argument: T); - fn push_pair(&mut self, key: T, value: T) { - self.push(key); - self.push(value); - } -} - -impl PushArgument for ArgumentList { - fn push(&mut self, argument: String) { - self.items.push(Argument::Owned(argument)) - } -} - -impl PushArgument<&'static str> for ArgumentList { - fn push(&mut self, argument: &'static str) { - self.items.push(Argument::Borrowed(argument)) - } -} - -#[derive(Debug, Clone)] -enum Argument { - Owned(String), - Borrowed(&'static str), -} - -impl Argument { - fn as_str(&self) -> &str { - match self { - Argument::Owned(owned) => &owned, - Argument::Borrowed(borrowed) => borrowed, - } - } -} - -pub fn build_qemu_command(config: &InstanceConfig) -> ArgumentList { - let mut cmd = ArgumentList::new("qemu-system-x86_64"); - cmd.pair("-name", format!("guest={},debug-threads=on", config.name)); - - cmd.push("-S"); - cmd.push("-no-user-config"); - cmd.push("-no-defaults"); - cmd.push("-no-shutdown"); - - if config.kvm { - cmd.push("-enable-kvm"); - } - - cmd.pair("-m", config.memory.to_string()); - - if config.uefi.enabled { - // OVMF will hang if S3 is not disabled - // disable S4 too, since libvirt does that 🤷 - // https://bugs.archlinux.org/task/59465#comment172528 - cmd.push_pair("-global", "ICH9-LPC.disable_s3=1"); - cmd.push_pair("-global", "ICH9-LPC.disable_s4=1"); - } - - cmd.push_pair("-rtc", "driftfix=slew"); - cmd.push_pair("-serial", "stdio"); - - #[cfg(any(target_arch = "x86_64", target_arch = "i686"))] - { - cmd.push_pair("-global", "kvm-pit.lost_tick_policy=discard") - } - - cmd.push("-no-hpet"); - cmd.push_pair("-boot", "strict=on"); - - cmd.pair( - "-smp", - format!( - "{},sockets={},dies={},cores={},threads={}", - config.cpu.amount, - config.cpu.sockets, - config.cpu.dies, - config.cpu.cores, - config.cpu.threads - ), - ); - - cmd.push_pair("-msg", "timestamp=on"); - - cmd -} - #[derive(Debug)] pub struct Qemu { process: Option, diff --git a/vored/src/main.rs b/vored/src/main.rs index e14facc..0a306f7 100644 --- a/vored/src/main.rs +++ b/vored/src/main.rs @@ -1,9 +1,10 @@ -use crate::instance::build_qemu_command; -use vore_core::InstanceConfig; +use vore_core::{build_qemu_command, GlobalConfig, InstanceConfig}; mod instance; fn main() { let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap(); - println!("Hello, world! {}", build_qemu_command(&cfg)); + let global = GlobalConfig::load(include_str!("../../config/global.toml")).unwrap(); + println!("Hello, world! {:?}", build_qemu_command(&cfg, &global)); + print!("hello world {:#?}", global); }