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.
This commit is contained in:
2026-09-21 01:03:21 +02:00
parent 5dd9dff76a
commit 810bf50814
+36 -2
View File
@@ -125,10 +125,16 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> {
veprintln!(" Kernel append: {}", kernel_append); veprintln!(" Kernel append: {}", kernel_append);
// Build QEMU arguments // Build QEMU arguments
// -machine virt is selected explicitly on riscv64 (see machine_for_arch)
// -display none suppresses VGA/BIOS output // -display none suppresses VGA/BIOS output
// -serial mon:stdio connects serial console to terminal with QEMU monitor muxed // -serial mon:stdio connects serial console to terminal with QEMU monitor muxed
// -no-reboot makes QEMU exit when the guest requests poweroff/reboot // -no-reboot makes QEMU exit when the guest requests poweroff/reboot
let mut args = vec![ let mut args: Vec<String> = 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(), "-kernel".to_string(),
config.kernel_path.to_string_lossy().to_string(), config.kernel_path.to_string_lossy().to_string(),
"-initrd".to_string(), "-initrd".to_string(),
@@ -146,7 +152,7 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> {
"user,id=net0".to_string(), "user,id=net0".to_string(),
"-device".to_string(), "-device".to_string(),
"virtio-net-pci,netdev=net0".to_string(), "virtio-net-pci,netdev=net0".to_string(),
]; ]);
// Add KVM acceleration if available // Add KVM acceleration if available
if use_kvm { if use_kvm {
@@ -182,6 +188,19 @@ fn qemu_binary_for_arch(arch: &str) -> String {
format!("qemu-system-{}", arch_enum.qemu_system_name()) 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 /// Get architecture suffix for package names
fn get_arch_package_suffix(arch: &str) -> &'static str { fn get_arch_package_suffix(arch: &str) -> &'static str {
crate::utils::Arch::from_str(arch).qemu_package_suffix() crate::utils::Arch::from_str(arch).qemu_package_suffix()
@@ -601,6 +620,21 @@ mod tests {
use super::*; use super::*;
use std::io::Read as _; 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 /// Parse a newc cpio archive into (name, ino, mode, nlink, file_size, data) tuples
fn parse_cpio(archive: Vec<u8>) -> Vec<(String, u32, u32, u32, u32, Vec<u8>)> { fn parse_cpio(archive: Vec<u8>) -> Vec<(String, u32, u32, u32, u32, Vec<u8>)> {
let mut cursor = std::io::Cursor::new(archive); let mut cursor = std::io::Cursor::new(archive);