aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2016-02-23 19:25:53 +0000
committerWill Deacon <will.deacon@arm.com>2016-03-02 02:25:49 +0000
commit52e326fb8595f012f850bdb715ddbb13ac38c9b5 (patch)
tree11cd9b345640fc38951a97d15e1b1bc4c9e10e0f
parentb3f606e18ed51577d2e7d56923ff8bc90c781738 (diff)
downloadkvmtool-52e326fb8595f012f850bdb715ddbb13ac38c9b5.tar.gz
arm64: Add PMUv3 support
In order to enable the in-kernel PMU emulation code, add a tiny bit of setup code that initializes the PMU on each CPU and populates the DT. The IRQ is harcoded to PPI7 (INTID23) in order to match what QEMU does. The code is enabled when the --pmu option is passed to lkvm. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--Makefile3
-rw-r--r--arm/aarch64/arm-cpu.c2
-rw-r--r--arm/aarch64/include/kvm/kvm-config-arch.h4
-rw-r--r--arm/aarch64/include/kvm/kvm-cpu-arch.h3
-rw-r--r--arm/include/arm-common/kvm-config-arch.h1
-rw-r--r--arm/include/arm-common/pmu.h4
-rw-r--r--arm/pmu.c77
7 files changed, 91 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 19eb7e5a..3d316067 100644
--- a/Makefile
+++ b/Makefile
@@ -153,7 +153,8 @@ endif
# ARM
OBJS_ARM_COMMON := arm/fdt.o arm/gic.o arm/ioport.o arm/irq.o \
- arm/kvm.o arm/kvm-cpu.o arm/pci.o arm/timer.o
+ arm/kvm.o arm/kvm-cpu.o arm/pci.o arm/timer.o \
+ arm/pmu.o
HDRS_ARM_COMMON := arm/include
ifeq ($(ARCH), arm)
DEFINES += -DCONFIG_ARM
diff --git a/arm/aarch64/arm-cpu.c b/arm/aarch64/arm-cpu.c
index 3dc8ea30..c21c0bb6 100644
--- a/arm/aarch64/arm-cpu.c
+++ b/arm/aarch64/arm-cpu.c
@@ -5,6 +5,7 @@
#include "arm-common/gic.h"
#include "arm-common/timer.h"
+#include "arm-common/pmu.h"
#include <linux/byteorder.h>
#include <linux/types.h>
@@ -14,6 +15,7 @@ static void generate_fdt_nodes(void *fdt, struct kvm *kvm, u32 gic_phandle)
int timer_interrupts[4] = {13, 14, 11, 10};
gic__generate_fdt_nodes(fdt, gic_phandle, kvm->cfg.arch.irqchip);
timer__generate_fdt_nodes(fdt, kvm, timer_interrupts);
+ pmu__generate_fdt_nodes(fdt, kvm);
}
static int arm_cpu__vcpu_init(struct kvm_cpu *vcpu)
diff --git a/arm/aarch64/include/kvm/kvm-config-arch.h b/arm/aarch64/include/kvm/kvm-config-arch.h
index 89860ae3..5ef1f17b 100644
--- a/arm/aarch64/include/kvm/kvm-config-arch.h
+++ b/arm/aarch64/include/kvm/kvm-config-arch.h
@@ -3,7 +3,9 @@
#define ARM_OPT_ARCH_RUN(cfg) \
OPT_BOOLEAN('\0', "aarch32", &(cfg)->aarch32_guest, \
- "Run AArch32 guest"),
+ "Run AArch32 guest"), \
+ OPT_BOOLEAN('\0', "pmu", &(cfg)->has_pmuv3, \
+ "Create PMUv3 device"),
#include "arm-common/kvm-config-arch.h"
diff --git a/arm/aarch64/include/kvm/kvm-cpu-arch.h b/arm/aarch64/include/kvm/kvm-cpu-arch.h
index 2ffa7ad2..a9d85633 100644
--- a/arm/aarch64/include/kvm/kvm-cpu-arch.h
+++ b/arm/aarch64/include/kvm/kvm-cpu-arch.h
@@ -7,7 +7,8 @@
#define ARM_VCPU_FEATURE_FLAGS(kvm, cpuid) { \
[0] = ((!!(cpuid) << KVM_ARM_VCPU_POWER_OFF) | \
- (!!(kvm)->cfg.arch.aarch32_guest << KVM_ARM_VCPU_EL1_32BIT)) \
+ (!!(kvm)->cfg.arch.aarch32_guest << KVM_ARM_VCPU_EL1_32BIT) | \
+ (!!(kvm)->cfg.arch.has_pmuv3 << KVM_ARM_VCPU_PMU_V3)) \
}
#define ARM_MPIDR_HWID_BITMASK 0xFF00FFFFFFUL
diff --git a/arm/include/arm-common/kvm-config-arch.h b/arm/include/arm-common/kvm-config-arch.h
index f9c59421..ed626b58 100644
--- a/arm/include/arm-common/kvm-config-arch.h
+++ b/arm/include/arm-common/kvm-config-arch.h
@@ -8,6 +8,7 @@ struct kvm_config_arch {
unsigned int force_cntfrq;
bool virtio_trans_pci;
bool aarch32_guest;
+ bool has_pmuv3;
enum irqchip_type irqchip;
};
diff --git a/arm/include/arm-common/pmu.h b/arm/include/arm-common/pmu.h
new file mode 100644
index 00000000..7a170a56
--- /dev/null
+++ b/arm/include/arm-common/pmu.h
@@ -0,0 +1,4 @@
+
+#define KVM_ARM_PMUv3_PPI 23
+
+void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm);
diff --git a/arm/pmu.c b/arm/pmu.c
new file mode 100644
index 00000000..8c810c16
--- /dev/null
+++ b/arm/pmu.c
@@ -0,0 +1,77 @@
+#include "kvm/fdt.h"
+#include "kvm/kvm.h"
+#include "kvm/kvm-cpu.h"
+#include "kvm/util.h"
+
+#include "arm-common/gic.h"
+#include "arm-common/pmu.h"
+
+static int set_pmu_attr(struct kvm *kvm, int vcpu_idx,
+ struct kvm_device_attr *attr)
+{
+ int ret, fd;
+
+ fd = kvm->cpus[vcpu_idx]->vcpu_fd;
+
+ ret = ioctl(fd, KVM_HAS_DEVICE_ATTR, attr);
+ if (!ret) {
+ ret = ioctl(fd, KVM_SET_DEVICE_ATTR, attr);
+ if (ret)
+ pr_err("PMU KVM_SET_DEVICE_ATTR failed (%d)\n", ret);
+ } else {
+ pr_err("Unsupported PMU on vcpu%d\n", vcpu_idx);
+ }
+
+ return ret;
+}
+
+void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm)
+{
+ const char compatible[] = "arm,armv8-pmuv3";
+ int irq = KVM_ARM_PMUv3_PPI;
+ int i, ret;
+
+ u32 cpu_mask = (((1 << kvm->nrcpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) \
+ & GIC_FDT_IRQ_PPI_CPU_MASK;
+ u32 irq_prop[] = {
+ cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI),
+ cpu_to_fdt32(irq - 16),
+ cpu_to_fdt32(cpu_mask | IRQ_TYPE_LEVEL_HIGH),
+ };
+
+ if (!kvm->cfg.arch.has_pmuv3)
+ return;
+
+ if (!kvm__supports_extension(kvm, KVM_CAP_ARM_PMU_V3)) {
+ pr_info("PMU unsupported\n");
+ return;
+ }
+
+ for (i = 0; i < kvm->nrcpus; i++) {
+ struct kvm_device_attr pmu_attr;
+
+ pmu_attr = (struct kvm_device_attr){
+ .group = KVM_ARM_VCPU_PMU_V3_CTRL,
+ .addr = (u64)(unsigned long)&irq,
+ .attr = KVM_ARM_VCPU_PMU_V3_IRQ,
+ };
+
+ ret = set_pmu_attr(kvm, i, &pmu_attr);
+ if (ret)
+ return;
+
+ pmu_attr = (struct kvm_device_attr){
+ .group = KVM_ARM_VCPU_PMU_V3_CTRL,
+ .attr = KVM_ARM_VCPU_PMU_V3_INIT,
+ };
+
+ ret = set_pmu_attr(kvm, i, &pmu_attr);
+ if (ret)
+ return;
+ }
+
+ _FDT(fdt_begin_node(fdt, "pmu"));
+ _FDT(fdt_property(fdt, "compatible", compatible, sizeof(compatible)));
+ _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
+ _FDT(fdt_end_node(fdt));
+}