From ba90e0f367aa052ff02cc34e8ad0ce22fd809aab Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Mon, 21 Sep 2026 01:24:11 +0200 Subject: [PATCH] fix(qemu): select the rva23s64 CPU for riscv64 under TCG Ubuntu builds its riscv64 port against the RVA23 profile (since 25.10), and QEMU's default rv64 CPU does not implement all profile extensions: Ubuntu binaries die with SIGILL during init and the kernel panics with 'Attempted to kill init'. Select the rva23s64 profile CPU (QEMU 10.1+) for riscv64 under TCG; profiles are supersets, so baseline rv64gc rootfses run unchanged on it. KVM mode keeps -cpu host. --- crates/ecr/src/qemu_vm.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/ecr/src/qemu_vm.rs b/crates/ecr/src/qemu_vm.rs index 9ee4816..66a39bf 100644 --- a/crates/ecr/src/qemu_vm.rs +++ b/crates/ecr/src/qemu_vm.rs @@ -159,6 +159,9 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> { args.push("-enable-kvm".to_string()); args.push("-cpu".to_string()); args.push("host".to_string()); + } else if let Some(cpu) = cpu_for_arch(&config.arch) { + args.push("-cpu".to_string()); + args.push(cpu.to_string()); } // Execute QEMU @@ -201,6 +204,22 @@ fn machine_for_arch(arch: &str) -> Option<&'static str> { } } +/// Get the QEMU TCG CPU model to select for the target architecture, when +/// the emulator default is not sufficient. +/// +/// Ubuntu builds its riscv64 port against the RVA23 profile (since 25.10), +/// so its binaries execute instructions QEMU's default `rv64` CPU does not +/// implement and die with SIGILL early in boot. `rva23s64` implements the +/// full supervisor profile; baseline rv64gc rootfses (Alpine, older +/// Ubuntu) run unchanged on it because profiles are supersets. The named +/// profile CPU needs QEMU 10.1+. +fn cpu_for_arch(arch: &str) -> Option<&'static str> { + match crate::utils::Arch::from_str(arch) { + crate::utils::Arch::Riscv64 => Some("rva23s64"), + _ => 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() @@ -635,6 +654,18 @@ mod tests { assert_eq!(machine_for_arch("s390x"), None); } + #[test] + fn test_cpu_for_arch() { + // Ubuntu's riscv64 userland needs the RVA23 profile CPU; QEMU's + // default `rv64` CPU SIGILLs on it early in boot + assert_eq!(cpu_for_arch("riscv64"), Some("rva23s64")); + // Other architectures keep their emulator's default CPU + assert_eq!(cpu_for_arch("amd64"), None); + assert_eq!(cpu_for_arch("x86_64"), None); + assert_eq!(cpu_for_arch("arm64"), None); + assert_eq!(cpu_for_arch("aarch64"), 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);