Some more progress: can now make valid cmd args
parent
92452ce4d6
commit
456587d166
@ -1,23 +1,6 @@
|
||||
[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"
|
||||
[uefi.default]
|
||||
boot-code = "/usr/share/OVMF/OVMF_CODE.fd"
|
||||
template = "/usr/share/OVMF/OVMF_VARS.fd"
|
@ -1,37 +1,187 @@
|
||||
function build_command(instance, args)
|
||||
args:add("-rtc", "driftfix=slew")
|
||||
args:add("-serial", "stdio")
|
||||
args:add("-no-hpet")
|
||||
args:add("-boot", "strict=on")
|
||||
---@param instance Instance
|
||||
---@return boolean
|
||||
function is_q35(instance)
|
||||
return (instance.chipset == "q35" or string.find(instance.chipset, "pc-q35") == 0)
|
||||
end
|
||||
|
||||
---@param instance Instance
|
||||
---@param vm VM
|
||||
---@return VM, string
|
||||
function ensure_pci(instance, vm)
|
||||
if is_q35(instance) then
|
||||
local i82801b11 = vm:get_device_id("i82801b11-bridge")
|
||||
if i82801b11 == nil then
|
||||
i82801b11 = "i82801b11"
|
||||
vm:arg("-device", "i82801b11-bridge,id=" .. i82801b11 .. ",bus=" .. vm:get_next_bus("pcie"))
|
||||
end
|
||||
|
||||
local pci_bridge = vm:get_device_id("pci-bridge")
|
||||
if pci_bridge == nil then
|
||||
pci_bridge = "pci-bridge"
|
||||
vm:arg("-device", "pci-bridge,chassis_nr=" .. vm:get_counter("chassis", 1) .. ",id=" .. pci_bridge .. ",bus=" .. i82801b11)
|
||||
end
|
||||
|
||||
return vm, pci_bridge
|
||||
else
|
||||
error("No support for non-q35 instances")
|
||||
end
|
||||
end
|
||||
|
||||
---@param instance Instance
|
||||
---@param vm VM
|
||||
---@param mem_path string
|
||||
---@param size number
|
||||
---@param id string
|
||||
---@return VM
|
||||
function add_shared_memory(instance, vm, mem_path, size, id)
|
||||
local pci
|
||||
vm, pci = ensure_pci(instance, vm)
|
||||
|
||||
vm:arg("-object", "memory-backend-file,id=shmem-" .. id .. ",mem-path=" .. mem_path .. ",size=" .. size .. ",share=on")
|
||||
vm:arg("-device", "ivshmem-plain,memdev=shmem-" .. id .. ",bus=" .. pci .. ",addr=0x" .. string.format("%x", vm:get_counter("pci", 1)))
|
||||
|
||||
return vm
|
||||
end
|
||||
|
||||
vore:set_build_command(function(instance, vm)
|
||||
vm:arg("-rtc", "driftfix=slew")
|
||||
--vm:arg("-mon", "stdio")
|
||||
vm:arg("-no-hpet")
|
||||
vm:arg("-boot", "strict=on")
|
||||
|
||||
vm:arg("-chardev", "socket,id=charmonitor,path=/tmp/qemu.sock,server=on,wait=off")
|
||||
vm:arg("-mon", "chardev=charmonitor,id=monitor,mode=readline")
|
||||
|
||||
if instance.kvm then
|
||||
args:add("-enable-kvm")
|
||||
vm:arg("-enable-kvm")
|
||||
end
|
||||
|
||||
if instance.arch == "x86_64" or instance.arch == "i868" then
|
||||
args:add("-global", "kvm-pit.lost_tick_policy=discard")
|
||||
vm:arg("-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))
|
||||
--this disables the QEMU GUI
|
||||
--vm:arg("-display", "none")
|
||||
|
||||
vm:arg("-no-user-config")
|
||||
--vm:arg("-nodefaults")
|
||||
vm:arg("-no-shutdown")
|
||||
vm:arg("-m", tostring(instance.memory))
|
||||
|
||||
local cpu = instance.cpu;
|
||||
args:add(string.format("%d,sockets=%d,dies=%d,cores=%d,threads=%d",
|
||||
vm:arg(
|
||||
"-smp",
|
||||
string.format(
|
||||
"%d,sockets=%d,dies=%d,cores=%d,threads=%d",
|
||||
cpu.amount,
|
||||
cpu.sockets,
|
||||
cpu.dies,
|
||||
cpu.cores,
|
||||
cpu.threads))
|
||||
cpu.threads
|
||||
)
|
||||
)
|
||||
|
||||
if instance.uefi.enabled and string.find(instance.chipset, "q35") == 0 then
|
||||
if instance.uefi.enabled and is_q35(instance) 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")
|
||||
vm:arg("-global", "ICH9-LPC.disable_s3=1")
|
||||
vm:arg("-global", "ICH9-LPC.disable_s4=1")
|
||||
end
|
||||
|
||||
for idx, disk in ipairs(instance.disks) do
|
||||
vm = vore:add_disk(vm, idx, disk)
|
||||
end
|
||||
|
||||
if instance.uefi.enabled then
|
||||
vm:arg(
|
||||
"-drive", "if=pflash,format=raw,unit=0,file=" .. global.uefi.default.boot_code .. ",readonly=on",
|
||||
"-drive", "if=pflash,format=raw,unit=1,file=" .. vore:get_file("uefi/OVMF_VARS.fd", global.uefi.default.template)
|
||||
)
|
||||
end
|
||||
|
||||
for _, vfio in ipairs(instance.vfio) do
|
||||
local def = "vfio-pci,host=" .. vfio.slot
|
||||
if vfio.graphics then
|
||||
def = def .. ",x-vga=on"
|
||||
end
|
||||
|
||||
if vfio.multifunction then
|
||||
def = def .. ",multifunction=on"
|
||||
end
|
||||
|
||||
if vfio.graphics and vm:get_counter("disabled_display", 0) == 0 then
|
||||
vm:arg("-vga", "none")
|
||||
end
|
||||
|
||||
vm:arg("-device", def)
|
||||
end
|
||||
|
||||
if instance.looking_glass.enabled then
|
||||
vm = add_shared_memory(instance, vm, instance.looking_glass.mem_path, instance.looking_glass.buffer_size, "lg")
|
||||
end
|
||||
|
||||
if instance.scream.enabled then
|
||||
vm = add_shared_memory(instance, vm, instance.scream.mem_path, instance.scream.buffer_size, "scream")
|
||||
end
|
||||
|
||||
if instance.spice.enabled then
|
||||
vm:arg("-spice", "unix,addr=" .. instance.spice.socket_path .. ",disable-ticketing=on,seamless-migration=on")
|
||||
end
|
||||
|
||||
return args
|
||||
vm:arg(
|
||||
"-machine",
|
||||
"q35,accel=kvm,usb=off,vmport=off,dump-guest-core=off,kernel_irqchip=on"
|
||||
)
|
||||
|
||||
vm:arg(
|
||||
"-cpu",
|
||||
"host,migratable=on,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=whatever,kvm=off"
|
||||
)
|
||||
|
||||
return vm
|
||||
end)
|
||||
|
||||
---
|
||||
---@param type string
|
||||
---@return fun(vm: VM, idx: number, disk: Disk): VM
|
||||
function scsi_disk_gen(type)
|
||||
return function(vm, idx, disk)
|
||||
vm:arg(
|
||||
"-blockdev",
|
||||
tojson({
|
||||
["driver"] = "raw",
|
||||
["file"] = {
|
||||
["driver"] = "host_device",
|
||||
["filename"] = disk.path,
|
||||
["aio"] = "native",
|
||||
["discard"] = "unmap",
|
||||
["cache"] = { ["direct"] = true, ["no-flush"] = false },
|
||||
},
|
||||
["node-name"] = "format-" .. idx,
|
||||
["read-only"] = false,
|
||||
["cache"] = { ["direct"] = true, ["no-flush"] = false },
|
||||
["discard"] = "unmap",
|
||||
})
|
||||
)
|
||||
|
||||
local scsi_pci = vm:get_device_id("virtio-scsi-pci")
|
||||
if scsi_pci == nil then
|
||||
scsi_pci = "scsi-pci"
|
||||
vm:arg("-device", "virtio-scsi-pci,id=" .. scsi_pci)
|
||||
end
|
||||
|
||||
local hd = "scsi-hd,drive=format-" .. idx .. ",bus=" .. scsi_pci .. ".0"
|
||||
if type == "ssd" then
|
||||
-- Having a rotation rate of 1 signals Windows it's an ssd
|
||||
hd = hd .. ",rotation_rate=1"
|
||||
end
|
||||
|
||||
vm:arg("-device", hd)
|
||||
|
||||
return vm
|
||||
end
|
||||
end
|
||||
|
||||
vore:register_disk_preset("ssd", scsi_disk_gen("ssd"))
|
||||
vore:register_disk_preset("hdd", scsi_disk_gen("hdd"))
|
@ -0,0 +1,123 @@
|
||||
--- Global configuration
|
||||
|
||||
---@class GlobalUefi
|
||||
---@field boot_code string
|
||||
---@field template string
|
||||
|
||||
---@class global
|
||||
---@field uefi table<string, GlobalUefi>
|
||||
global = {}
|
||||
|
||||
---@class Vore
|
||||
vore = {}
|
||||
|
||||
---@class VM
|
||||
VM = {}
|
||||
|
||||
----
|
||||
---Encodes input as json (implemented in Rust)
|
||||
---@param input any
|
||||
---@return string
|
||||
function tojson(input)
|
||||
end
|
||||
|
||||
---Add an argument to the current argument list for this vm
|
||||
---@vararg string
|
||||
function VM:arg(...)
|
||||
end
|
||||
|
||||
---Get the next available bus for a post
|
||||
---@param port string
|
||||
---@return string
|
||||
function VM:get_next_bus(port)
|
||||
end
|
||||
|
||||
---Get a new counter that +1 every cal
|
||||
---@field name string
|
||||
---@field default number
|
||||
---@return number
|
||||
function VM:get_counter(name, default)
|
||||
end
|
||||
|
||||
---Get the last device id of a added device
|
||||
---@param device_name string
|
||||
---@return string
|
||||
function VM:get_device_id(device_name)
|
||||
end
|
||||
|
||||
---@class Disk
|
||||
---@field preset string
|
||||
---@field disk_type string
|
||||
---@field path string
|
||||
|
||||
|
||||
---@class Cpu
|
||||
---@field amount number
|
||||
---@field sockets number
|
||||
---@field dies number
|
||||
---@field cores number
|
||||
---@field threads number
|
||||
|
||||
---@class Uefi
|
||||
---@field enabled boolean
|
||||
|
||||
---@class LookingGlass
|
||||
---@field enabled boolean
|
||||
---@field mem_path string
|
||||
---@field buffer_size number
|
||||
|
||||
---@class Scream
|
||||
---@field enabled boolean
|
||||
---@field mem_path string
|
||||
---@field buffer_size number
|
||||
|
||||
---@class Vfio
|
||||
---@field slot string
|
||||
---@field graphics boolean
|
||||
---@field multifunction boolean
|
||||
|
||||
---@class Spice
|
||||
---@field enabled boolean
|
||||
---@field socket_path string
|
||||
|
||||
---@class Instance
|
||||
---@field name string
|
||||
---@field kvm boolean
|
||||
---@field arch string
|
||||
---@field memory number
|
||||
---@field chipset string
|
||||
---@field disks Disk[]
|
||||
---@field cpu Cpu
|
||||
---@field uefi Uefi
|
||||
---@field vfio Vfio[]
|
||||
---@field looking_glass LookingGlass
|
||||
---@field scream Scream
|
||||
---@field spice Spice
|
||||
|
||||
----
|
||||
---Add a disk definition to the argument list
|
||||
---@param vm VM
|
||||
---@param index number
|
||||
---@param disk Disk
|
||||
---@return VM
|
||||
function vore:add_disk(vm, index, disk)
|
||||
end
|
||||
|
||||
----
|
||||
---Register a disk preset
|
||||
---@param name string
|
||||
---@param cb fun(vm: VM, idx: number, disk: Disk): VM
|
||||
function vore:register_disk_preset(name, cb)
|
||||
end
|
||||
|
||||
---set_build_command
|
||||
---@param cb fun(instance: Instance, vm: VM)
|
||||
function vore:set_build_command(cb)
|
||||
end
|
||||
|
||||
---Get a local file based from a template
|
||||
---If the target file doesn't exist yet it will be created from the source file
|
||||
---@param target string The target path within the local working directory
|
||||
---@param source_file string the source or template file
|
||||
function vore:get_file(target, source_file)
|
||||
end
|
@ -1,7 +1,8 @@
|
||||
mod global_config;
|
||||
mod instance_config;
|
||||
mod qemu;
|
||||
mod virtual_machine;
|
||||
|
||||
pub use global_config::*;
|
||||
pub use instance_config::*;
|
||||
pub use qemu::build_qemu_command;
|
||||
pub use qemu::QemuCommandBuilder;
|
||||
|
@ -0,0 +1,38 @@
|
||||
use crate::{GlobalConfig, InstanceConfig, QemuCommandBuilder};
|
||||
use std::option::Option::Some;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VirtualMachine {
|
||||
working_dir: PathBuf,
|
||||
config: InstanceConfig,
|
||||
process: Option<Child>,
|
||||
}
|
||||
|
||||
impl VirtualMachine {
|
||||
pub fn new(config: InstanceConfig, working_dir: PathBuf) -> VirtualMachine {
|
||||
VirtualMachine {
|
||||
working_dir,
|
||||
config,
|
||||
process: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, global_config: &GlobalConfig) -> Result<(), anyhow::Error> {
|
||||
if let Some(proc) = &mut self.process {
|
||||
if proc.try_wait()?.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let builder = QemuCommandBuilder::new(global_config, self.working_dir.clone())?;
|
||||
let cmd = builder.build(&self.config)?;
|
||||
|
||||
let mut command = Command::new("qemu-system-x86_64");
|
||||
command.args(cmd);
|
||||
self.process = Some(command.spawn()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
use std::process::Child;
|
||||
use vore_core::InstanceConfig;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Instance {
|
||||
config: InstanceConfig,
|
||||
qemu: Option<Qemu>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn from_config(config: InstanceConfig) -> Instance {
|
||||
Instance { config, qemu: None }
|
||||
}
|
||||
|
||||
pub fn spawn_qemu(&self) -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Qemu {
|
||||
process: Option<Child>,
|
||||
}
|
@ -1,10 +1,23 @@
|
||||
use vore_core::{build_qemu_command, GlobalConfig, InstanceConfig};
|
||||
|
||||
mod instance;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use vore_core::{GlobalConfig, InstanceConfig, QemuCommandBuilder};
|
||||
|
||||
fn main() {
|
||||
let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap();
|
||||
println!("CONFIG:\n{:#?}", cfg);
|
||||
let global = GlobalConfig::load(include_str!("../../config/global.toml")).unwrap();
|
||||
println!("Hello, world! {:?}", build_qemu_command(&cfg, &global));
|
||||
print!("hello world {:#?}", global);
|
||||
let builder =
|
||||
QemuCommandBuilder::new(&global, PathBuf::from("/home/eater/.lib/vore/win10")).unwrap();
|
||||
let command = builder.build(&cfg).unwrap();
|
||||
// .iter()
|
||||
// .map(|x| format!("'{}'", x))
|
||||
// .collect::<Vec<_>>()
|
||||
// .join(" ");
|
||||
|
||||
Command::new("qemu-system-x86_64")
|
||||
.args(command)
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
Loading…
Reference in New Issue