From 6bd6f2cf77104430a868f4a36e805246f28a95ed Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Wed, 17 Jun 2026 16:42:34 +0200 Subject: [PATCH] feat: enable kvm with --kernel if relevant --- src/qemu_vm.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/qemu_vm.rs b/src/qemu_vm.rs index 8998926..78be5ff 100644 --- a/src/qemu_vm.rs +++ b/src/qemu_vm.rs @@ -39,7 +39,7 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { )); } - // Create a gzipped cpio initramfs from the rootfs + // Create an uncompressed cpio initramfs from the rootfs let initramfs = create_initramfs(&config.rootfs_path)?; // Get QEMU binary for architecture @@ -54,6 +54,14 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { qemu_bin, get_arch_package_suffix(&config.arch), get_arch_package_suffix(&config.arch), get_arch_package_suffix(&config.arch) ))?; + // Check if we can use KVM acceleration + let use_kvm = can_use_kvm(&config.arch); + if use_kvm { + veprintln!(" KVM: enabled (native acceleration)"); + } else { + veprintln!(" KVM: disabled (using software emulation)"); + } + // Detect the best available shell in the rootfs let shell = crate::utils::detect_shell(&config.rootfs_path); @@ -93,7 +101,7 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { // -display none suppresses VGA/BIOS output // -serial mon:stdio connects serial console to terminal with QEMU monitor muxed // -no-reboot makes QEMU exit when the guest requests poweroff/reboot - let args = vec![ + let mut args = vec![ "-kernel".to_string(), config.kernel_path.to_string_lossy().to_string(), "-initrd".to_string(), @@ -113,6 +121,13 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { "virtio-net-pci,netdev=net0".to_string(), ]; + // Add KVM acceleration if available + if use_kvm { + args.push("-enable-kvm".to_string()); + args.push("-cpu".to_string()); + args.push("host".to_string()); + } + // Execute QEMU let status = Command::new(&qemu_bin) .args(&args) @@ -160,6 +175,59 @@ fn get_arch_package_suffix(arch: &str) -> &str { } } +/// Check if KVM acceleration can be used for the target architecture +fn can_use_kvm(target_arch: &str) -> bool { + // Normalize both to canonical form (uname -m style) and compare + let host_arch = get_host_arch(); + let target_canonical = normalize_arch(target_arch); + + if host_arch != target_canonical { + return false; + } + + // Check if /dev/kvm exists and is accessible + std::fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/kvm") + .is_ok() +} + +/// Normalize architecture name to canonical form (uname -m style) +fn normalize_arch(arch: &str) -> String { + match arch { + "amd64" => "x86_64", + "arm64" => "aarch64", + "ppc64el" => "ppc64le", + "armhf" => "armv7l", + other => other, + }.to_string() +} + +/// Get the host system architecture (uname -m style) +fn get_host_arch() -> String { + // Use uname to get the machine hardware name + match std::process::Command::new("uname").arg("-m").output() { + Ok(output) => { + String::from_utf8_lossy(&output.stdout).trim().to_string() + } + Err(_) => { + // Fallback: use libc uname + let mut utsname: libc::utsname = unsafe { std::mem::zeroed() }; + if unsafe { libc::uname(&mut utsname) } == 0 { + let machine: Vec = utsname.machine + .iter() + .take_while(|&&c| c != 0) + .map(|&c| c as u8) + .collect(); + String::from_utf8_lossy(&machine).into_owned() + } else { + "unknown".to_string() + } + } + } +} + /// Create an uncompressed cpio initramfs from a directory fn create_initramfs(rootfs: &PathBuf) -> Result { // Create a temporary file for the initramfs (uncompressed cpio)