summary refs log tree commit diff
diff options
context:
space:
mode:
authorSuleiman Souhlal <suleiman@google.com>2020-04-21 14:11:47 +0900
committerCommit Bot <commit-bot@chromium.org>2020-05-08 03:25:38 +0000
commit7dc915dd2af19ed4dbafb6b1b6e86733af5a922d (patch)
treed1820536e6f433eaf155b259c9d831b9360df807
parenta1c0e3c680c44ae4a949744b27f3cf0f7ea77939 (diff)
downloadcrosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar.gz
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar.bz2
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar.lz
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar.xz
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.tar.zst
crosvm-7dc915dd2af19ed4dbafb6b1b6e86733af5a922d.zip
aarch64: Enable PMU in the guest.
This allows us to use perf/simpleperf in the guest.

BUG=b:153708112
TEST=Use simpleperf in ARCVM guest on jacuzzi board.

Change-Id: Ia3d7dc5bcd3ca033ddf05b5ee2593102c98e8b49
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2156592
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Suleiman Souhlal <suleiman@chromium.org>
Reviewed-by: Sonny Rao <sonnyrao@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Commit-Queue: Suleiman Souhlal <suleiman@chromium.org>
-rw-r--r--aarch64/src/fdt.rs23
-rw-r--r--aarch64/src/lib.rs17
-rw-r--r--kvm/src/cap.rs1
-rw-r--r--kvm/src/lib.rs44
-rw-r--r--kvm_sys/src/aarch64/bindings.rs5
-rw-r--r--kvm_sys/src/x86/bindings.rs1
6 files changed, 89 insertions, 2 deletions
diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs
index fc976ef..19662c6 100644
--- a/aarch64/src/fdt.rs
+++ b/aarch64/src/fdt.rs
@@ -42,6 +42,8 @@ use crate::AARCH64_MMIO_SIZE;
 use crate::AARCH64_PCI_CFG_BASE;
 use crate::AARCH64_PCI_CFG_SIZE;
 
+use crate::AARCH64_PMU_IRQ;
+
 // This is an arbitrary number to specify the node for the GIC.
 // If we had a more complex interrupt architecture, then we'd need an enum for
 // these.
@@ -137,6 +139,23 @@ fn create_timer_node(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
     Ok(())
 }
 
+fn create_pmu_node(fdt: &mut Vec<u8>, num_cpus: u32) -> Result<()> {
+    let compatible = "arm,armv8-pmuv3";
+    let cpu_mask: u32 =
+        (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
+    let irq = generate_prop32(&[
+        GIC_FDT_IRQ_TYPE_PPI,
+        AARCH64_PMU_IRQ,
+        cpu_mask | IRQ_TYPE_LEVEL_HIGH,
+    ]);
+
+    begin_node(fdt, "pmu")?;
+    property_string(fdt, "compatible", compatible)?;
+    property(fdt, "interrupts", &irq)?;
+    end_node(fdt)?;
+    Ok(())
+}
+
 fn create_serial_node(fdt: &mut Vec<u8>, addr: u64, irq: u32) -> Result<()> {
     let serial_reg_prop = generate_prop64(&[addr, AARCH64_SERIAL_SIZE]);
     let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING]);
@@ -351,6 +370,7 @@ pub fn create_fdt(
     initrd: Option<(GuestAddress, usize)>,
     android_fstab: Option<File>,
     is_gicv3: bool,
+    use_pmu: bool,
 ) -> Result<()> {
     let mut fdt = vec![0; fdt_max_size];
     start_fdt(&mut fdt, fdt_max_size)?;
@@ -369,6 +389,9 @@ pub fn create_fdt(
     create_cpu_nodes(&mut fdt, num_cpus)?;
     create_gic_node(&mut fdt, is_gicv3, num_cpus as u64)?;
     create_timer_node(&mut fdt, num_cpus)?;
+    if use_pmu {
+        create_pmu_node(&mut fdt, num_cpus)?;
+    }
     create_serial_nodes(&mut fdt)?;
     create_psci_node(&mut fdt)?;
     create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?;
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index 4c8af6b..0a90fd4 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -109,6 +109,9 @@ const AARCH64_MMIO_SIZE: u64 = 0x100000;
 // Virtio devices start at SPI interrupt number 3
 const AARCH64_IRQ_BASE: u32 = 3;
 
+// PMU PPI interrupt, same as qemu
+const AARCH64_PMU_IRQ: u32 = 7;
+
 #[sorted]
 #[derive(Debug)]
 pub enum Error {
@@ -237,6 +240,11 @@ impl arch::LinuxArch for AArch64 {
 
         let (irq_chip, is_gicv3) = Self::create_irq_chip(&vm, vcpu_count as u64)?;
 
+        let mut use_pmu = true;
+        for vcpu in &vcpus {
+            use_pmu &= vcpu.arm_pmu_init(AARCH64_PMU_IRQ as u64 + 16).is_ok();
+        }
+
         let mut mmio_bus = devices::Bus::new();
 
         let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
@@ -315,6 +323,7 @@ impl arch::LinuxArch for AArch64 {
             components.android_fstab,
             kernel_end,
             is_gicv3,
+            use_pmu,
         )?;
 
         Ok(RunnableLinuxVm {
@@ -346,6 +355,7 @@ impl AArch64 {
         android_fstab: Option<File>,
         kernel_end: u64,
         is_gicv3: bool,
+        use_pmu: bool,
     ) -> Result<()> {
         let initrd = match initrd_file {
             Some(initrd_file) => {
@@ -374,6 +384,7 @@ impl AArch64 {
             initrd,
             android_fstab,
             is_gicv3,
+            use_pmu,
         )
         .map_err(Error::CreateFdt)?;
         Ok(())
@@ -540,7 +551,7 @@ impl AArch64 {
 
     fn configure_vcpu(
         guest_mem: &GuestMemory,
-        _kvm: &Kvm,
+        kvm: &Kvm,
         vm: &Vm,
         vcpu: &Vcpu,
         cpu_id: u64,
@@ -555,8 +566,10 @@ impl AArch64 {
         vm.arm_preferred_target(&mut kvi)
             .map_err(Error::ReadPreferredTarget)?;
 
-        // TODO(sonnyrao): need to verify this feature is supported by host kernel
         kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_PSCI_0_2;
+        if kvm.check_extension(Cap::ArmPmuV3) {
+            kvi.features[0] |= 1 << kvm_sys::KVM_ARM_VCPU_PMU_V3;
+        }
 
         // Non-boot cpus are powered off initially
         if cpu_id > 0 {
diff --git a/kvm/src/cap.rs b/kvm/src/cap.rs
index 7dfd965..ff65b59 100644
--- a/kvm/src/cap.rs
+++ b/kvm/src/cap.rs
@@ -119,4 +119,5 @@ pub enum Cap {
     CheckExtensionVm = KVM_CAP_CHECK_EXTENSION_VM,
     S390UserSigp = KVM_CAP_S390_USER_SIGP,
     ImmediateExit = KVM_CAP_IMMEDIATE_EXIT,
+    ArmPmuV3 = KVM_CAP_ARM_PMU_V3,
 }
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index b8cd12b..8b98b7c 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -1763,6 +1763,50 @@ impl Vcpu {
         if ret < 0 {
             return errno_result();
         }
+
+        Ok(())
+    }
+
+    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+    pub fn arm_pmu_init(&self, irq: u64) -> Result<()> {
+        let irq_addr = &irq as *const u64;
+
+        // The in-kernel PMU virtualization is initialized by setting the irq
+        // with KVM_ARM_VCPU_PMU_V3_IRQ and then by KVM_ARM_VCPU_PMU_V3_INIT.
+
+        let irq_attr = kvm_device_attr {
+            group: kvm_sys::KVM_ARM_VCPU_PMU_V3_CTRL,
+            attr: kvm_sys::KVM_ARM_VCPU_PMU_V3_IRQ as u64,
+            addr: irq_addr as u64,
+            flags: 0,
+        };
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_HAS_DEVICE_ATTR(), &irq_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &irq_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
+        let init_attr = kvm_device_attr {
+            group: kvm_sys::KVM_ARM_VCPU_PMU_V3_CTRL,
+            attr: kvm_sys::KVM_ARM_VCPU_PMU_V3_INIT as u64,
+            addr: 0,
+            flags: 0,
+        };
+        // safe becuase we allocated the struct and we know the kernel will read
+        // exactly the size of the struct
+        let ret = unsafe { ioctl_with_ref(self, kvm_sys::KVM_SET_DEVICE_ATTR(), &init_attr) };
+        if ret < 0 {
+            return errno_result();
+        }
+
         Ok(())
     }
 
diff --git a/kvm_sys/src/aarch64/bindings.rs b/kvm_sys/src/aarch64/bindings.rs
index 084fc13..0dd7f5a 100644
--- a/kvm_sys/src/aarch64/bindings.rs
+++ b/kvm_sys/src/aarch64/bindings.rs
@@ -157,6 +157,10 @@ pub const KVM_VGIC_V3_ADDR_TYPE_REDIST: ::std::os::raw::c_uint = 3;
 pub const KVM_ARM_VCPU_POWER_OFF: ::std::os::raw::c_uint = 0;
 pub const KVM_ARM_VCPU_EL1_32BIT: ::std::os::raw::c_uint = 1;
 pub const KVM_ARM_VCPU_PSCI_0_2: ::std::os::raw::c_uint = 2;
+pub const KVM_ARM_VCPU_PMU_V3: ::std::os::raw::c_uint = 3;
+pub const KVM_ARM_VCPU_PMU_V3_CTRL: ::std::os::raw::c_uint = 0;
+pub const KVM_ARM_VCPU_PMU_V3_IRQ: ::std::os::raw::c_uint = 0;
+pub const KVM_ARM_VCPU_PMU_V3_INIT: ::std::os::raw::c_uint = 1;
 pub const KVM_ARM_MAX_DBG_REGS: ::std::os::raw::c_uint = 16;
 pub const KVM_GUESTDBG_USE_SW_BP: ::std::os::raw::c_uint = 65536;
 pub const KVM_GUESTDBG_USE_HW: ::std::os::raw::c_uint = 131072;
@@ -439,6 +443,7 @@ pub const KVM_CAP_GUEST_DEBUG_HW_BPS: ::std::os::raw::c_uint = 119;
 pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
 pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
 pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
+pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
 pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
 pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;
 pub const KVM_IRQ_ROUTING_MSI: ::std::os::raw::c_uint = 2;
diff --git a/kvm_sys/src/x86/bindings.rs b/kvm_sys/src/x86/bindings.rs
index 7236611..629d83a 100644
--- a/kvm_sys/src/x86/bindings.rs
+++ b/kvm_sys/src/x86/bindings.rs
@@ -426,6 +426,7 @@ pub const KVM_CAP_GUEST_DEBUG_HW_WPS: ::std::os::raw::c_uint = 120;
 pub const KVM_CAP_SPLIT_IRQCHIP: ::std::os::raw::c_uint = 121;
 pub const KVM_CAP_IOEVENTFD_ANY_LENGTH: ::std::os::raw::c_uint = 122;
 pub const KVM_CAP_HYPERV_SYNIC: ::std::os::raw::c_uint = 123;
+pub const KVM_CAP_ARM_PMU_V3: ::std::os::raw::c_uint = 126;
 pub const KVM_CAP_IMMEDIATE_EXIT: ::std::os::raw::c_uint = 136;
 pub const KVM_CAP_HYPERV_SYNIC2: ::std::os::raw::c_uint = 148;
 pub const KVM_IRQ_ROUTING_IRQCHIP: ::std::os::raw::c_uint = 1;