diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2016-02-23 19:25:53 +0000 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-03-02 02:25:49 +0000 |
commit | 52e326fb8595f012f850bdb715ddbb13ac38c9b5 (patch) | |
tree | 11cd9b345640fc38951a97d15e1b1bc4c9e10e0f | |
parent | b3f606e18ed51577d2e7d56923ff8bc90c781738 (diff) | |
download | kvmtool-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-- | Makefile | 3 | ||||
-rw-r--r-- | arm/aarch64/arm-cpu.c | 2 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/kvm-config-arch.h | 4 | ||||
-rw-r--r-- | arm/aarch64/include/kvm/kvm-cpu-arch.h | 3 | ||||
-rw-r--r-- | arm/include/arm-common/kvm-config-arch.h | 1 | ||||
-rw-r--r-- | arm/include/arm-common/pmu.h | 4 | ||||
-rw-r--r-- | arm/pmu.c | 77 |
7 files changed, 91 insertions, 3 deletions
@@ -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)); +} |