diff --git a/arch/src/aarch64/mod.rs b/arch/src/aarch64/mod.rs index 2d9caa353..954c40bc3 100644 --- a/arch/src/aarch64/mod.rs +++ b/arch/src/aarch64/mod.rs @@ -43,6 +43,9 @@ pub enum Error { /// Error configuring the MPIDR register VcpuRegMpidr(hypervisor::HypervisorCpuError), + + /// Error initializing PMU for vcpu + VcpuInitPmu, } impl From for super::Error { diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 9d66e93a6..ac5ae69c4 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -104,6 +104,9 @@ pub enum Error { #[cfg(feature = "tdx")] InitializeTdx(hypervisor::HypervisorCpuError), + #[cfg(target_arch = "aarch64")] + InitPmu(hypervisor::HypervisorCpuError), + /// Failed scheduling the thread on the expected CPU set. ScheduleCpuSet, } @@ -312,6 +315,7 @@ impl Vcpu { .map_err(Error::VcpuArmPreferredTarget)?; // We already checked that the capability is supported. kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; // Non-boot cpus are powered off initially. if self.id > 0 { kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; @@ -699,6 +703,51 @@ impl CpuManager { Ok(()) } + #[cfg(target_arch = "aarch64")] + pub fn init_pmu(&self, irq: u32) -> Result { + let cpu_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_ARM_VCPU_PMU_V3_CTRL, + attr: u64::from(kvm_bindings::KVM_ARM_VCPU_PMU_V3_INIT), + addr: 0x0, + flags: 0, + }; + + for cpu in self.vcpus.iter() { + let tmp = irq; + let cpu_attr_irq = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_ARM_VCPU_PMU_V3_CTRL, + attr: u64::from(kvm_bindings::KVM_ARM_VCPU_PMU_V3_IRQ), + addr: &tmp as *const u32 as u64, + flags: 0, + }; + + // Check if PMU attr is available, if not, log the information. + if cpu.lock().unwrap().vcpu.has_vcpu_attr(&cpu_attr).is_ok() { + // Set irq for PMU + cpu.lock() + .unwrap() + .vcpu + .set_vcpu_attr(&cpu_attr_irq) + .map_err(Error::InitPmu)?; + + // Init PMU + cpu.lock() + .unwrap() + .vcpu + .set_vcpu_attr(&cpu_attr) + .map_err(Error::InitPmu)?; + } else { + debug!( + "PMU attribute is not supported in vCPU{}, skip PMU init!", + cpu.lock().unwrap().id + ); + return Ok(false); + } + } + + Ok(true) + } + fn start_vcpu( &mut self, vcpu: Arc>, diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 82998df3d..f4f456822 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1151,6 +1151,16 @@ impl Vm { Error::ConfigureSystem(arch::Error::AArch64Setup(arch::aarch64::Error::SetupGic(e))) })?; + // PMU interrupt sticks to PPI, so need to be added by 16 to get real irq number. + let pmu_supported = self + .cpu_manager + .lock() + .unwrap() + .init_pmu(arch::aarch64::fdt::AARCH64_PMU_IRQ + 16) + .map_err(|_| { + Error::ConfigureSystem(arch::Error::AArch64Setup(arch::aarch64::Error::VcpuInitPmu)) + })?; + arch::configure_system( &mem, cmdline.as_str(),