From 14aa30cd2e34b5b5ac801af3b866789b1c862938 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Wed, 17 Jun 2026 19:11:10 +0800 Subject: [PATCH] vmm: retrieve timebase-frequency from KVM instead of hardcoding The RISC-V device tree's timebase-frequency was hardcoded to 10 MHz (0x989680). Actual hardware uses different frequencies. Read the timebase frequency from KVM_GET_ONE_REG via KVM_REG_RISCV_TIMER (offset 0, kvm_riscv_timer.frequency), thread it through the VMM to arch to FDT layers, and fall back to the 10 MHz default when KVM returns no value. Signed-off-by: Meng Zhuo --- arch/src/riscv64/fdt.rs | 12 ++++++++---- arch/src/riscv64/mod.rs | 2 ++ hypervisor/src/cpu.rs | 8 ++++++++ hypervisor/src/kvm/mod.rs | 14 +++++++++++++- vmm/src/cpu.rs | 5 +++++ vmm/src/vm.rs | 11 +++++++++++ 6 files changed, 47 insertions(+), 5 deletions(-) diff --git a/arch/src/riscv64/fdt.rs b/arch/src/riscv64/fdt.rs index f05b0200f..c418b3217 100644 --- a/arch/src/riscv64/fdt.rs +++ b/arch/src/riscv64/fdt.rs @@ -71,6 +71,7 @@ pub fn create_fdt>, initrd: &Option, pci_space_info: &[PciSpaceInfo], + timebase_frequency: u32, ) -> FdtWriterResult> { // Allocate stuff necessary for the holding the blob. let mut fdt = FdtWriter::new()?; @@ -86,7 +87,7 @@ pub fn create_fdt Res } // 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, isa_string: &str) -> FdtWriterResult<()> { +fn create_cpu_nodes( + fdt: &mut FdtWriter, + num_cpus: u32, + isa_string: &str, + timebase_frequency: u32, +) -> 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. fdt.property_u32("#address-cells", 0x01)?; fdt.property_u32("#size-cells", 0x0)?; - // TODO: Retrieve CPU frequency from cpu timer regs - let timebase_frequency: u32 = 0x989680; fdt.property_u32("timebase-frequency", timebase_frequency)?; for cpu_index in 0..num_cpus { diff --git a/arch/src/riscv64/mod.rs b/arch/src/riscv64/mod.rs index ad00a4ddb..8530b4f33 100644 --- a/arch/src/riscv64/mod.rs +++ b/arch/src/riscv64/mod.rs @@ -168,6 +168,7 @@ pub fn configure_system, pci_space_info: &[PciSpaceInfo], aia_device: &Arc>, + timebase_frequency: u32, ) -> super::Result<()> { let isa_string = isa_string_from_host()?; let fdt_final = fdt::create_fdt( @@ -179,6 +180,7 @@ pub fn configure_system Result; /// + /// Get the timebase frequency (timer frequency in Hz) on RISC-V 64-bit. + /// This is the frequency at which the RISC-V `time` CSR increments. + /// + #[cfg(target_arch = "riscv64")] + fn get_timebase_frequency(&self) -> Result { + Ok(0) + } + /// /// Configure core registers for a given CPU. /// #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index d031d335a..e61987ea7 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -124,7 +124,7 @@ use kvm_bindings::{ KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, KVM_REG_SIZE_U128, kvm_regs, user_pt_regs, }; #[cfg(target_arch = "riscv64")] -use kvm_bindings::{KVM_REG_RISCV_CORE, kvm_riscv_core}; +use kvm_bindings::{KVM_REG_RISCV_CORE, KVM_REG_RISCV_TIMER, kvm_riscv_core}; #[cfg(feature = "tdx")] use kvm_bindings::{KVM_X86_SW_PROTECTED_VM, KVMIO}; #[cfg(target_arch = "x86_64")] @@ -2730,6 +2730,18 @@ impl cpu::Vcpu for KvmVcpu { unimplemented!() } + #[cfg(target_arch = "riscv64")] + fn get_timebase_frequency(&self) -> cpu::Result { + use kvm_bindings::kvm_riscv_timer; + let freq_offset = offset_of!(kvm_riscv_timer, frequency); + let id = riscv64_reg_id!(KVM_REG_RISCV_TIMER, freq_offset); + let mut freq_bytes = [0u8; 8]; + self.fd + .get_one_reg(id, &mut freq_bytes) + .map_err(|e| cpu::HypervisorCpuError::GetNonCoreRegister(e.into()))?; + Ok(u64::from_le_bytes(freq_bytes)) + } + /// /// Configure core registers for a given CPU. /// diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index d1adc613f..599335600 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -642,6 +642,11 @@ impl Vcpu { self.vcpu.run() } + #[cfg(target_arch = "riscv64")] + pub fn get_timebase_frequency(&self) -> result::Result { + self.vcpu.get_timebase_frequency() + } + #[cfg(feature = "sev_snp")] pub fn set_sev_control_register(&self, vmsa_pfn: u64) -> Result<()> { self.vcpu diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 667a3910c..d17b92b71 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1988,6 +1988,16 @@ impl Vm { // TODO: PMU support for riscv64 is scheduled to next stage. + let timebase_frequency = self + .cpu_manager + .lock() + .unwrap() + .vcpus() + .first() + .and_then(|vcpu| vcpu.lock().unwrap().get_timebase_frequency().ok()) + .map(|f| f as u32) + .unwrap_or(0x989680); + arch::configure_system( &mem, cmdline.as_cstring().unwrap().to_str().unwrap(), @@ -1996,6 +2006,7 @@ impl Vm { &initramfs_config, &pci_space_info, &vaia, + timebase_frequency, ) .map_err(Error::ConfigureSystem)?;