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