Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "qemu"
|
||||
version = "0.1.0"
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "qemu"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
use std::{path::PathBuf, process::Command};
|
||||
|
||||
use crate::{Architecture, IntoArgs};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Machine {
|
||||
Virt { virtualize: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cpu {
|
||||
CortexA57,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Image {
|
||||
Kernel {
|
||||
kernel: PathBuf,
|
||||
initrd: Option<PathBuf>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QemuAArch64;
|
||||
|
||||
impl IntoArgs for Machine {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-M");
|
||||
match self {
|
||||
&Self::Virt { virtualize } => {
|
||||
let mut arg = "virt".to_owned();
|
||||
if virtualize {
|
||||
arg.push_str(",virtualization=on");
|
||||
}
|
||||
command.arg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Cpu {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-cpu");
|
||||
match self {
|
||||
Self::CortexA57 => {
|
||||
command.arg("cortex-a57");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Image {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::Kernel { kernel, initrd } => {
|
||||
command.arg("-kernel").arg(kernel);
|
||||
if let Some(initrd) = initrd {
|
||||
command.arg("-initrd").arg(initrd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuAArch64 {
|
||||
fn add_args(&self, _command: &mut Command) {}
|
||||
}
|
||||
|
||||
impl Architecture for QemuAArch64 {
|
||||
type MachineType = Machine;
|
||||
type CpuType = Cpu;
|
||||
type ImageType = Image;
|
||||
|
||||
const DEFAULT_COMMAND: &'static str = "qemu-system-aarch64";
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{aarch64::Machine, device::QemuSerialTarget, Qemu};
|
||||
|
||||
use super::{Cpu, Image};
|
||||
|
||||
#[test]
|
||||
fn boot_options() {
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.with_boot_image(Image::Kernel {
|
||||
kernel: "my-kernel.bin".into(),
|
||||
initrd: Some("my-initrd.img".into()),
|
||||
});
|
||||
let command = qemu.into_command();
|
||||
|
||||
assert_eq!(
|
||||
command.get_args().collect::<Vec<_>>(),
|
||||
vec!["-kernel", "my-kernel.bin", "-initrd", "my-initrd.img"]
|
||||
);
|
||||
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.with_boot_image(Image::Kernel {
|
||||
kernel: "my-kernel.bin".into(),
|
||||
initrd: None,
|
||||
});
|
||||
let command = qemu.into_command();
|
||||
|
||||
assert_eq!(
|
||||
command.get_args().collect::<Vec<_>>(),
|
||||
vec!["-kernel", "my-kernel.bin"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn machine_options() {
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.with_machine(Machine::Virt { virtualize: true });
|
||||
|
||||
let command = qemu.into_command();
|
||||
|
||||
assert_eq!(
|
||||
command.get_args().collect::<Vec<_>>(),
|
||||
vec!["-M", "virt,virtualization=on"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_commands() {
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.override_qemu("my-qemu");
|
||||
qemu.with_boot_image(Image::Kernel {
|
||||
kernel: "my-kernel.bin".into(),
|
||||
initrd: Some("my-initrd.img".into()),
|
||||
});
|
||||
qemu.with_machine(Machine::Virt { virtualize: false });
|
||||
qemu.with_cpu(Cpu::CortexA57);
|
||||
qemu.with_serial(QemuSerialTarget::MonStdio);
|
||||
|
||||
let command = qemu.into_command();
|
||||
|
||||
assert_eq!(
|
||||
command.get_args().collect::<Vec<_>>(),
|
||||
vec![
|
||||
"-serial",
|
||||
"mon:stdio",
|
||||
"-M",
|
||||
"virt",
|
||||
"-cpu",
|
||||
"cortex-a57",
|
||||
"-kernel",
|
||||
"my-kernel.bin",
|
||||
"-initrd",
|
||||
"my-initrd.img"
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use std::process::Command;
|
||||
|
||||
use crate::IntoArgs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum QemuNic {
|
||||
VirtioPci { mac: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum QemuDevice {
|
||||
NetworkTap {
|
||||
nic: QemuNic,
|
||||
script: Option<String>,
|
||||
ifname: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum QemuSerialTarget {
|
||||
MonStdio,
|
||||
Stdio,
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuNic {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::VirtioPci { mac } => {
|
||||
command.arg("-device");
|
||||
let mut val = "virtio-net-pci,netdev=net0".to_owned();
|
||||
if let Some(mac) = mac {
|
||||
val.push_str(",mac=");
|
||||
val.push_str(mac);
|
||||
}
|
||||
command.arg(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO separate handling for devices
|
||||
impl IntoArgs for QemuDevice {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::NetworkTap {
|
||||
nic,
|
||||
script,
|
||||
ifname,
|
||||
} => {
|
||||
command.arg("-netdev");
|
||||
let mut val = "tap,id=net0".to_owned();
|
||||
if let Some(script) = script {
|
||||
val.push_str(",script=");
|
||||
val.push_str(script);
|
||||
}
|
||||
if let Some(ifname) = ifname {
|
||||
val.push_str(",ifname=");
|
||||
val.push_str(ifname);
|
||||
}
|
||||
command.arg(val);
|
||||
|
||||
nic.add_args(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuSerialTarget {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-serial");
|
||||
match self {
|
||||
Self::MonStdio => {
|
||||
command.arg("mon:stdio");
|
||||
}
|
||||
Self::Stdio => {
|
||||
command.arg("stdio");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
use std::{fmt::Debug, path::PathBuf, process::Command};
|
||||
|
||||
use device::{QemuDevice, QemuSerialTarget};
|
||||
|
||||
pub mod aarch64;
|
||||
pub mod x86_64;
|
||||
|
||||
pub mod device;
|
||||
|
||||
pub trait Architecture: IntoArgs {
|
||||
type MachineType: IntoArgs;
|
||||
type CpuType: IntoArgs;
|
||||
type ImageType: IntoArgs;
|
||||
|
||||
const DEFAULT_COMMAND: &'static str;
|
||||
}
|
||||
|
||||
pub trait IntoArgs {
|
||||
fn add_args(&self, command: &mut Command);
|
||||
}
|
||||
|
||||
impl<T: IntoArgs> IntoArgs for Option<T> {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
if let Some(val) = self {
|
||||
val.add_args(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct QemuCommon {
|
||||
devices: Vec<QemuDevice>,
|
||||
serial: Option<QemuSerialTarget>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Qemu<A: Architecture> {
|
||||
binary_override: Option<PathBuf>,
|
||||
common: QemuCommon,
|
||||
cpu: Option<A::CpuType>,
|
||||
machine: Option<A::MachineType>,
|
||||
boot_image: Option<A::ImageType>,
|
||||
no_display: bool,
|
||||
memory_megabytes: Option<usize>,
|
||||
smp: Option<usize>,
|
||||
arch: A,
|
||||
}
|
||||
|
||||
impl Qemu<aarch64::QemuAArch64> {
|
||||
pub fn new_aarch64() -> Self {
|
||||
Qemu::new(aarch64::QemuAArch64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Qemu<x86_64::QemuX86_64> {
|
||||
pub fn new_x86_64() -> Self {
|
||||
Qemu::new(x86_64::QemuX86_64::default())
|
||||
}
|
||||
|
||||
pub fn with_bios_image(&mut self, image: PathBuf) -> &mut Self {
|
||||
self.arch.bios_image = Some(image);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_boot_slot(&mut self, slot: char) -> &mut Self {
|
||||
self.arch.boot_slot = Some(slot);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> Qemu<A> {
|
||||
pub fn new(arch: A) -> Self {
|
||||
Self {
|
||||
binary_override: None,
|
||||
common: QemuCommon::default(),
|
||||
|
||||
memory_megabytes: None,
|
||||
no_display: false,
|
||||
cpu: None,
|
||||
machine: None,
|
||||
boot_image: None,
|
||||
smp: None,
|
||||
arch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_device(&mut self, device: QemuDevice) -> &mut Self {
|
||||
self.common.devices.push(device);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_memory_megabytes(&mut self, value: usize) -> &mut Self {
|
||||
self.memory_megabytes = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_serial(&mut self, serial: QemuSerialTarget) -> &mut Self {
|
||||
self.common.serial = Some(serial);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_boot_image(&mut self, image: A::ImageType) -> &mut Self {
|
||||
self.boot_image = Some(image);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cpu(&mut self, cpu: A::CpuType) -> &mut Self {
|
||||
self.cpu = Some(cpu);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_machine(&mut self, machine: A::MachineType) -> &mut Self {
|
||||
self.machine = Some(machine);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disable_display(&mut self) -> &mut Self {
|
||||
self.no_display = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn override_qemu<S: Into<PathBuf>>(&mut self, qemu: S) -> &mut Self {
|
||||
self.binary_override = Some(qemu.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_command(self) -> Command {
|
||||
let qemu = self
|
||||
.binary_override
|
||||
.clone()
|
||||
.unwrap_or_else(|| A::DEFAULT_COMMAND.into());
|
||||
|
||||
let mut command = Command::new(qemu);
|
||||
|
||||
self.add_args(&mut command);
|
||||
|
||||
command
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuCommon {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
self.serial.add_args(command);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> IntoArgs for Qemu<A> {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
self.common.add_args(command);
|
||||
|
||||
for device in &self.common.devices {
|
||||
device.add_args(command);
|
||||
}
|
||||
|
||||
if let Some(mem) = self.memory_megabytes {
|
||||
command.args(["-m", &format!("{}m", mem)]);
|
||||
}
|
||||
|
||||
self.machine.add_args(command);
|
||||
self.cpu.add_args(command);
|
||||
self.boot_image.add_args(command);
|
||||
|
||||
if self.no_display {
|
||||
command.args(["-display", "none"]);
|
||||
}
|
||||
|
||||
if let Some(smp) = self.smp {
|
||||
command.args(["-smp", &format!("{}", smp)]);
|
||||
}
|
||||
|
||||
self.arch.add_args(command);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{device::QemuSerialTarget, Qemu};
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let builder = Qemu::new_aarch64();
|
||||
let command = builder.into_command();
|
||||
assert_eq!(command.get_program(), "qemu-system-aarch64");
|
||||
assert_eq!(command.get_args().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qemu_override() {
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.override_qemu("test-qemu");
|
||||
let command = qemu.into_command();
|
||||
assert_eq!(command.get_program(), "test-qemu");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn common_options() {
|
||||
let mut qemu = Qemu::new_aarch64();
|
||||
qemu.with_serial(QemuSerialTarget::MonStdio);
|
||||
let command = qemu.into_command();
|
||||
|
||||
assert_eq!(
|
||||
command.get_args().collect::<Vec<_>>(),
|
||||
vec!["-serial", "mon:stdio"]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use std::{path::PathBuf, process::Command};
|
||||
|
||||
use crate::{Architecture, IntoArgs};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Machine {
|
||||
Q35,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cpu {
|
||||
Host { enable_kvm: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Image {
|
||||
Drive(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct QemuX86_64 {
|
||||
pub bios_image: Option<PathBuf>,
|
||||
pub boot_slot: Option<char>,
|
||||
}
|
||||
|
||||
impl IntoArgs for Machine {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::Q35 => {
|
||||
command.args(["-M", "q35"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Cpu {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
&Self::Host { enable_kvm } => {
|
||||
command.args(["-cpu", "host"]);
|
||||
if enable_kvm {
|
||||
command.arg("-enable-kvm");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Image {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
match self {
|
||||
Self::Drive(path) => {
|
||||
command.args(["-drive", &format!("format=raw,file={}", path.display())]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuX86_64 {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
if let Some(slot) = self.boot_slot {
|
||||
command.arg("-boot");
|
||||
command.arg(format!("{}", slot));
|
||||
}
|
||||
|
||||
if let Some(bios_image) = self.bios_image.as_ref() {
|
||||
command.args([
|
||||
"-drive",
|
||||
&format!(
|
||||
"format=raw,if=pflash,readonly=on,file={}",
|
||||
bios_image.display()
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Architecture for QemuX86_64 {
|
||||
type MachineType = Machine;
|
||||
type CpuType = Cpu;
|
||||
type ImageType = Image;
|
||||
|
||||
const DEFAULT_COMMAND: &'static str = "qemu-system-x86_64";
|
||||
}
|
||||
Reference in New Issue
Block a user