@@ -1093,6 +1093,7 @@ struct ArchCPU {
/* Allows to override the default configuration */
uint8_t num_bps;
uint8_t num_wps;
+ int8_t num_pmu_ctrs;
};
typedef struct ARMCPUInfo {
@@ -2312,6 +2313,8 @@ FIELD(MFAR, FPA, 12, 40)
FIELD(MFAR, NSE, 62, 1)
FIELD(MFAR, NS, 63, 1)
+FIELD(PMCR, N, 11, 5)
+
QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK);
/* If adding a feature bit which corresponds to a Linux ELF
@@ -17,6 +17,7 @@
#define KVM_ARM_VGIC_V3 (1 << 1)
#define KVM_REG_ARM_ID_AA64DFR0_EL1 ARM64_SYS_REG(3, 0, 0, 5, 0)
+#define KVM_REG_ARM_PMCR_EL0 ARM64_SYS_REG(3, 3, 9, 12, 0)
/**
* kvm_arm_register_device:
@@ -95,7 +95,7 @@ static const char *cpu_model_advertised_features[] = {
"sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048",
"kvm-no-adjvtime", "kvm-steal-time",
"pauth", "pauth-impdef", "pauth-qarma3",
- "num-breakpoints", "num-watchpoints",
+ "num-breakpoints", "num-watchpoints", "num-pmu-counters",
NULL
};
@@ -638,12 +638,53 @@ static void arm_cpu_set_num_bps(Object *obj, Visitor *v, const char *name,
cpu->num_bps = val;
}
+static void arm_cpu_get_num_pmu_ctrs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ if (cpu->num_pmu_ctrs == -1) {
+ val = FIELD_EX64(cpu->isar.reset_pmcr_el0, PMCR, N);
+ } else {
+ val = cpu->num_pmu_ctrs;
+ }
+
+ visit_type_uint8(v, name, &val, errp);
+}
+
+static void arm_cpu_set_num_pmu_ctrs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint8_t val;
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint8_t max_ctrs = FIELD_EX64(cpu->isar.reset_pmcr_el0, PMCR, N);
+
+ if (!visit_type_uint8(v, name, &val, errp)) {
+ return;
+ }
+
+ if (val > max_ctrs) {
+ error_setg(errp, "invalid number of PMU counters");
+ return;
+ }
+
+ cpu->num_pmu_ctrs = val;
+}
+
static void aarch64_add_kvm_writable_properties(Object *obj)
{
+ ARMCPU *cpu = ARM_CPU(obj);
+
object_property_add(obj, "num-breakpoints", "uint8", arm_cpu_get_num_bps,
arm_cpu_set_num_bps, NULL, NULL);
object_property_add(obj, "num-watchpoints", "uint8", arm_cpu_get_num_wps,
arm_cpu_set_num_wps, NULL, NULL);
+
+ cpu->num_pmu_ctrs = -1;
+ object_property_add(obj, "num-pmu-counters", "uint8",
+ arm_cpu_get_num_pmu_ctrs, arm_cpu_set_num_pmu_ctrs,
+ NULL, NULL);
}
#endif /* CONFIG_KVM */
@@ -418,7 +418,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
if (pmu_supported) {
/* PMCR_EL0 is only accessible if the vCPU has feature PMU_V3 */
err |= read_sys_reg64(fdarray[2], &ahcf->isar.reset_pmcr_el0,
- ARM64_SYS_REG(3, 3, 9, 12, 0));
+ KVM_REG_ARM_PMCR_EL0);
}
if (sve_supported) {
@@ -919,9 +919,41 @@ static void kvm_arm_configure_aa64dfr0(ARMCPU *cpu)
}
}
+static void kvm_arm_configure_pmcr(ARMCPU *cpu)
+{
+ int ret;
+ uint64_t val, newval;
+ CPUState *cs = CPU(cpu);
+
+ if (cpu->num_pmu_ctrs == -1) {
+ return;
+ }
+
+ newval = FIELD_DP64(cpu->isar.reset_pmcr_el0, PMCR, N, cpu->num_pmu_ctrs);
+ ret = kvm_set_one_reg(cs, KVM_REG_ARM_PMCR_EL0, &newval);
+ if (ret) {
+ error_report("Failed to set KVM_REG_ARM_PMCR_EL0");
+ return;
+ }
+
+ /*
+ * Check if the write succeeded, since older versions of KVM ignore it.
+ */
+ ret = kvm_get_one_reg(cs, KVM_REG_ARM_PMCR_EL0, &val);
+ if (ret) {
+ error_report("Failed to get KVM_REG_ARM_PMCR_EL0");
+ return;
+ }
+
+ if (val != newval) {
+ error_report("Failed to update KVM_REG_ARM_PMCR_EL0");
+ }
+}
+
static void kvm_arm_configure_vcpu_regs(ARMCPU *cpu)
{
kvm_arm_configure_aa64dfr0(cpu);
+ kvm_arm_configure_pmcr(cpu);
}
/**
Add a "num-pmu-counters" CPU parameter to configure the number of counters that KVM presents to the guest. This is needed for Realm VMs, whose parameters include the number of PMU counters and influence the Realm Initial Measurement. Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> --- v1->v2: new --- target/arm/cpu.h | 3 +++ target/arm/kvm_arm.h | 1 + target/arm/arm-qmp-cmds.c | 2 +- target/arm/cpu64.c | 41 +++++++++++++++++++++++++++++++++++++++ target/arm/kvm.c | 34 +++++++++++++++++++++++++++++++- 5 files changed, 79 insertions(+), 2 deletions(-)