diff --git a/arch/src/riscv64/fdt.rs b/arch/src/riscv64/fdt.rs index ee453eb2f..e78138861 100644 --- a/arch/src/riscv64/fdt.rs +++ b/arch/src/riscv64/fdt.rs @@ -65,6 +65,7 @@ pub fn create_fdt, aia_device: &Arc>, initrd: &Option, @@ -84,7 +85,7 @@ pub fn create_fdt, guest_mem: &GuestMemoryMmap) -> R } // Following are the auxiliary function for creating the different nodes that we append to our FDT. -fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> { +fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32, isa_string: &str) -> FdtWriterResult<()> { // See https://elixir.bootlin.com/linux/v6.10/source/Documentation/devicetree/bindings/riscv/cpus.yaml let cpus = fdt.begin_node("cpus")?; // As per documentation, on RISC-V 64-bit systems value should be set to 1. @@ -123,7 +124,7 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> { fdt.property_string("device_type", "cpu")?; fdt.property_string("compatible", "riscv")?; fdt.property_string("mmu-type", "sv48")?; - fdt.property_string("riscv,isa", "rv64imafdc_smaia_ssaia")?; + fdt.property_string("riscv,isa", isa_string)?; fdt.property_string("status", "okay")?; fdt.property_u32("reg", cpu_index)?; fdt.property_u32("phandle", CPU_BASE_PHANDLE + cpu_index)?; diff --git a/arch/src/riscv64/mod.rs b/arch/src/riscv64/mod.rs index b4478aaa6..b4feb99d5 100644 --- a/arch/src/riscv64/mod.rs +++ b/arch/src/riscv64/mod.rs @@ -12,6 +12,8 @@ pub mod uefi; use std::collections::HashMap; use std::fmt::Debug; +use std::fs::File; +use std::io::{BufRead, BufReader}; use std::sync::{Arc, Mutex}; use hypervisor::arch::riscv64::aia::Vaia; @@ -51,6 +53,22 @@ pub enum Error { /// Error configuring the general purpose registers #[error("Error configuring the general purpose registers")] RegsConfiguration(#[source] hypervisor::HypervisorCpuError), + + /// Error opening /proc/cpuinfo + #[error("Error opening /proc/cpuinfo")] + OpenCpuInfo(#[source] std::io::Error), + + /// Error reading /proc/cpuinfo + #[error("Error reading /proc/cpuinfo")] + ReadCpuInfo(#[source] std::io::Error), + + /// Invalid ISA string + #[error("Invalid ISA string: {0}")] + InvalidIsaString(String), + + /// Error parsing /proc/cpuinfo + #[error("Error parsing /proc/cpuinfo")] + CpuInfoParsing, } #[derive(Debug, Copy, Clone)] @@ -104,6 +122,43 @@ pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> { ] } +// Read the first "isa" string from /proc/cpuinfo and filter out the H extension, +// while correctly preserving multi-letter extensions. +fn isa_string_from_host() -> Result { + let file = File::open("/proc/cpuinfo").map_err(Error::OpenCpuInfo)?; + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line.map_err(Error::ReadCpuInfo)?; + let trimmed_line = line.trim(); + + if trimmed_line.starts_with("isa") { + let parts: Vec<&str> = trimmed_line.split(':').collect(); + if parts.len() == 2 { + let isa_string = parts[1].trim(); + + // Split the string by underscores to separate single letter vs long-form + // extensions + let mut components: Vec = + isa_string.split('_').map(|s| s.to_string()).collect(); + + if components.is_empty() { + return Err(Error::InvalidIsaString(isa_string.to_string())); + } + + // Remove H extension if present in single letter extensions + let first_component = components[0].chars().filter(|&c| c != 'h').collect(); + + components[0] = first_component; + + return Ok(components.join("_")); + } + } + } + + Err(Error::CpuInfoParsing) +} + /// Configures the system and should be called once per vm before starting vcpu threads. #[allow(clippy::too_many_arguments)] pub fn configure_system( @@ -115,10 +170,12 @@ pub fn configure_system>, ) -> super::Result<()> { + let isa_string = isa_string_from_host()?; let fdt_final = fdt::create_fdt( guest_mem, cmdline, num_vcpu, + &isa_string, device_info, aia_device, initrd,