Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/kit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ libc = "0.2"
camino = "1.1.12"
comfy-table = "7.1"
strum = { version = "0.26", features = ["derive"] }
quick-xml = "0.36"

[dev-dependencies]
similar-asserts = "1.5"
Expand Down
139 changes: 94 additions & 45 deletions crates/kit/src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! This module provides cross-architecture support for libvirt domain creation
//! and QEMU emulator selection, avoiding hardcoded architecture assumptions.

use crate::xml_utils::XmlWriter;
use color_eyre::Result;

/// Architecture configuration for libvirt domains and QEMU
Expand Down Expand Up @@ -44,52 +45,33 @@ impl ArchConfig {
}
}

/// Get architecture-specific XML features for libvirt
pub fn xml_features(&self) -> &'static str {
match self.arch {
"x86_64" => {
r#"
<features>
<acpi/>
<apic/>
<vmport state='off'/>
</features>"#
}
"aarch64" => {
r#"
<features>
<acpi/>
<apic/>
</features>"#
}
_ => {
r#"
<features>
<acpi/>
<apic/>
</features>"#
}
/// Generate architecture-specific XML features for libvirt
pub fn write_features(&self, writer: &mut XmlWriter) -> Result<()> {
writer.start_element("features", &[])?;
writer.write_empty_element("acpi", &[])?;
writer.write_empty_element("apic", &[])?;

// Add x86_64-specific features
if self.arch == "x86_64" {
writer.write_empty_element("vmport", &[("state", "off")])?;
}

writer.end_element("features")?;
Ok(())
}

/// Get architecture-specific timer configuration
pub fn xml_timers(&self) -> &'static str {
match self.arch {
"x86_64" => {
r#"
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>"#
}
"aarch64" => {
r#"
<timer name='rtc' tickpolicy='catchup'/>"#
}
_ => {
r#"
<timer name='rtc' tickpolicy='catchup'/>"#
}
/// Generate architecture-specific timer configuration
pub fn write_timers(&self, writer: &mut XmlWriter) -> Result<()> {
// RTC timer is common to all architectures
writer.write_empty_element("timer", &[("name", "rtc"), ("tickpolicy", "catchup")])?;

// Add x86_64-specific timers
if self.arch == "x86_64" {
writer.write_empty_element("timer", &[("name", "pit"), ("tickpolicy", "delay")])?;
writer.write_empty_element("timer", &[("name", "hpet"), ("present", "no")])?;
}

Ok(())
}

/// Check if this architecture supports VMport (x86_64 specific feature)
Expand Down Expand Up @@ -146,9 +128,20 @@ mod tests {
fn test_arch_specific_features() {
let arch_config = ArchConfig::detect().unwrap();

// All architectures should have some features
assert!(!arch_config.xml_features().is_empty());
assert!(!arch_config.xml_timers().is_empty());
// Test that we can generate features XML without errors
let mut writer = XmlWriter::new();
arch_config.write_features(&mut writer).unwrap();
let features_xml = writer.into_string().unwrap();
assert!(features_xml.contains("<features>"));
assert!(features_xml.contains("<acpi/>"));
assert!(features_xml.contains("</features>"));

// Test that we can generate timers XML without errors
let mut writer = XmlWriter::new();
arch_config.write_timers(&mut writer).unwrap();
let timers_xml = writer.into_string().unwrap();
assert!(timers_xml.contains("timer"));
assert!(timers_xml.contains("rtc"));

// CPU mode should be valid
assert!(!arch_config.cpu_mode().is_empty());
Expand Down Expand Up @@ -177,4 +170,60 @@ mod tests {
// Should be mutually exclusive
assert!(!(is_x86_64() && is_aarch64()));
}

/// Helper function to generate XML for testing
fn generate_xml<F>(config: &ArchConfig, writer_fn: F) -> String
where
F: FnOnce(&ArchConfig, &mut XmlWriter) -> Result<()>,
{
let mut writer = XmlWriter::new();
writer_fn(config, &mut writer).unwrap();
writer.into_string().unwrap()
}

#[test]
fn test_x86_64_specific_features() {
// Test x86_64 configuration specifically
let x86_config = ArchConfig {
arch: "x86_64",
machine: "q35",
os_type: "hvm",
};

let features_xml = generate_xml(&x86_config, |cfg, w| cfg.write_features(w));

// Should have x86_64-specific vmport feature
assert!(features_xml.contains("vmport"));
assert!(features_xml.contains("state=\"off\""));

let timers_xml = generate_xml(&x86_config, |cfg, w| cfg.write_timers(w));

// Should have x86_64-specific timers
assert!(timers_xml.contains("pit"));
assert!(timers_xml.contains("hpet"));
assert!(timers_xml.contains("present=\"no\""));
}

#[test]
fn test_aarch64_specific_features() {
// Test aarch64 configuration specifically
let arm_config = ArchConfig {
arch: "aarch64",
machine: "virt",
os_type: "hvm",
};

let features_xml = generate_xml(&arm_config, |cfg, w| cfg.write_features(w));

// Should NOT have x86_64-specific vmport feature
assert!(!features_xml.contains("vmport"));

let timers_xml = generate_xml(&arm_config, |cfg, w| cfg.write_timers(w));

// Should NOT have x86_64-specific timers
assert!(!timers_xml.contains("pit"));
assert!(!timers_xml.contains("hpet"));
// But should still have RTC
assert!(timers_xml.contains("rtc"));
}
}
Loading