From 810bf5081457fd3bfed91a6129446a6e6572768d Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Mon, 21 Sep 2026 01:03:21 +0200 Subject: [PATCH] fix(qemu): select the virt machine for riscv64 qemu-system-riscv64's default machine is spike, not virt: spike has no PCI bus, so virtio-net-pci failed with "No 'PCI' bus found", and no 16550 UART, so console=ttyS0 output went nowhere. Pass -machine virt explicitly for riscv64; other architectures keep their emulator's default. --- crates/ecr/src/qemu_vm.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/crates/ecr/src/qemu_vm.rs b/crates/ecr/src/qemu_vm.rs index abe870f..9ee4816 100644 --- a/crates/ecr/src/qemu_vm.rs +++ b/crates/ecr/src/qemu_vm.rs @@ -125,10 +125,16 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { veprintln!(" Kernel append: {}", kernel_append); // Build QEMU arguments + // -machine virt is selected explicitly on riscv64 (see machine_for_arch) // -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 mut args = vec![ + let mut args: Vec = Vec::new(); + if let Some(machine) = machine_for_arch(&config.arch) { + args.push("-machine".to_string()); + args.push(machine.to_string()); + } + args.extend(vec![ "-kernel".to_string(), config.kernel_path.to_string_lossy().to_string(), "-initrd".to_string(), @@ -146,7 +152,7 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { "user,id=net0".to_string(), "-device".to_string(), "virtio-net-pci,netdev=net0".to_string(), - ]; + ]); // Add KVM acceleration if available if use_kvm { @@ -182,6 +188,19 @@ fn qemu_binary_for_arch(arch: &str) -> String { format!("qemu-system-{}", arch_enum.qemu_system_name()) } +/// Get the QEMU machine to select for the target architecture, if any. +/// +/// qemu-system-riscv64's default machine is `spike`, which has neither a +/// PCI bus (virtio-net-pci fails with "No 'PCI' bus found") nor a 16550 +/// UART (console=ttyS0 output goes nowhere); the `virt` board has both, +/// plus bundled OpenSBI firmware for -kernel boot. +fn machine_for_arch(arch: &str) -> Option<&'static str> { + match crate::utils::Arch::from_str(arch) { + crate::utils::Arch::Riscv64 => Some("virt"), + _ => None, + } +} + /// Get architecture suffix for package names fn get_arch_package_suffix(arch: &str) -> &'static str { crate::utils::Arch::from_str(arch).qemu_package_suffix() @@ -601,6 +620,21 @@ mod tests { use super::*; use std::io::Read as _; + #[test] + fn test_machine_for_arch() { + // riscv64 must select the virt machine explicitly: the + // qemu-system-riscv64 default is `spike`, which has no PCI bus and + // no 16550 UART + assert_eq!(machine_for_arch("riscv64"), Some("virt")); + // Other architectures keep their emulator's default machine + assert_eq!(machine_for_arch("amd64"), None); + assert_eq!(machine_for_arch("x86_64"), None); + assert_eq!(machine_for_arch("arm64"), None); + assert_eq!(machine_for_arch("aarch64"), None); + assert_eq!(machine_for_arch("ppc64le"), None); + assert_eq!(machine_for_arch("s390x"), None); + } + /// Parse a newc cpio archive into (name, ino, mode, nlink, file_size, data) tuples fn parse_cpio(archive: Vec) -> Vec<(String, u32, u32, u32, u32, Vec)> { let mut cursor = std::io::Cursor::new(archive);