* [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3
@ 2024-08-27 0:22 Oliver Upton
2024-08-27 0:22 ` [PATCH v2 01/12] KVM: arm64: Rename kvm_pmu_valid_counter_mask() Oliver Upton
` (11 more replies)
0 siblings, 12 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Well, looks like that grew a bit :)
The coarse-grained traps for the PMU available at EL2 are annoyingly
effective in both guest and host contexts, meaning the traps need to be
evaluated for a VM's host EL0. Yuck.
First couple patches align the PMU emulation w/ the architecture, where
a PMU has an implemented set of counters *and* a dynamic view of said
counters based on the current context.
Patches 2-9 add support for Host EL0 traps to the NV trap
infrastructure, and takes advantage of it for MDCR_EL2.{TPM, TPMCR,
HPMN}.
The whole interaction of CGTs declaring their EL0-ness is a bit shaky,
since it is a property of the triaged sysreg itself. I structured it
this way for the mostly theoretical problem of a sysreg having multiple
traps, only some of which take effect in Host EL0.
Last bit is the event filter fixes I had from before.
v1: https://lore.kernel.org/kvmarm/20240824001402.3909504-1-oliver.upton@linux.dev/
Oliver Upton (12):
KVM: arm64: Rename kvm_pmu_valid_counter_mask()
KVM: arm64: nv: Adjust range of accessible PMCs according to HPMN
KVM: arm64: nv: Rename BEHAVE_FORWARD_ANY
KVM: arm64: nv: Add support for describing traps that affect Host EL0
KVM: arm64: nv: Reinject traps that take effect in Host EL0
KVM: arm64: nv: Honor MDCR_EL2.{TPM, TPMCR} in Host EL0
KVM: arm64: nv: Allow coarse-grained trap combos to use complex traps
KVM: arm64: nv: Describe trap behaviour of MDCR_EL2.HPMN
KVM: arm64: nv: Advertise support for FEAT_HPMN0
KVM: arm64: Add helpers to determine if PMC counts at a given EL
KVM: arm64: nv: Honor NSH filter when in hyp context
KVM: arm64: nv: Reprogram PMU events affected by nested transition
arch/arm64/include/asm/kvm_emulate.h | 5 +
arch/arm64/kvm/emulate-nested.c | 319 +++++++++++++++++----------
arch/arm64/kvm/nested.c | 5 +-
arch/arm64/kvm/pmu-emul.c | 97 ++++++--
arch/arm64/kvm/sys_regs.c | 12 +-
include/kvm/arm_pmu.h | 12 +-
6 files changed, 311 insertions(+), 139 deletions(-)
base-commit: 7c626ce4bae1ac14f60076d00eafe71af30450ba
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 01/12] KVM: arm64: Rename kvm_pmu_valid_counter_mask()
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 02/12] KVM: arm64: nv: Adjust range of accessible PMCs according to HPMN Oliver Upton
` (10 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Nested PMU support requires dynamically changing the visible range of
PMU counters based on the exception level and value of MDCR_EL2.HPMN. At
the same time, the PMU emulation code needs to know the absolute number
of implemented counters, regardless of context.
Rename the existing helper to make it obvious that it returns the number
of implemented counters and not anything else.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/pmu-emul.c | 8 ++++----
arch/arm64/kvm/sys_regs.c | 12 ++++++------
include/kvm/arm_pmu.h | 4 ++--
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 82a2a003259c..e88d6bd8221a 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -244,7 +244,7 @@ void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu)
*/
void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu)
{
- unsigned long mask = kvm_pmu_valid_counter_mask(vcpu);
+ unsigned long mask = kvm_pmu_implemented_counter_mask(vcpu);
int i;
for_each_set_bit(i, &mask, 32)
@@ -265,7 +265,7 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
irq_work_sync(&vcpu->arch.pmu.overflow_work);
}
-u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
+u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
{
u64 val = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));
@@ -574,7 +574,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0);
if (val & ARMV8_PMU_PMCR_P) {
- unsigned long mask = kvm_pmu_valid_counter_mask(vcpu);
+ unsigned long mask = kvm_pmu_implemented_counter_mask(vcpu);
mask &= ~BIT(ARMV8_PMU_CYCLE_IDX);
for_each_set_bit(i, &mask, 32)
kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, i), 0, true);
@@ -804,7 +804,7 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
{
- u64 mask = kvm_pmu_valid_counter_mask(vcpu);
+ u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu));
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index c90324060436..b25f6f44f451 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1128,7 +1128,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va
{
bool set;
- val &= kvm_pmu_valid_counter_mask(vcpu);
+ val &= kvm_pmu_implemented_counter_mask(vcpu);
switch (r->reg) {
case PMOVSSET_EL0:
@@ -1151,7 +1151,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va
static int get_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 *val)
{
- u64 mask = kvm_pmu_valid_counter_mask(vcpu);
+ u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
*val = __vcpu_sys_reg(vcpu, r->reg) & mask;
return 0;
@@ -1165,7 +1165,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (pmu_access_el0_disabled(vcpu))
return false;
- mask = kvm_pmu_valid_counter_mask(vcpu);
+ mask = kvm_pmu_implemented_counter_mask(vcpu);
if (p->is_write) {
val = p->regval & mask;
if (r->Op2 & 0x1) {
@@ -1188,7 +1188,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 mask = kvm_pmu_valid_counter_mask(vcpu);
+ u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
if (check_pmu_access_disabled(vcpu, 0))
return false;
@@ -1212,7 +1212,7 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 mask = kvm_pmu_valid_counter_mask(vcpu);
+ u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
if (pmu_access_el0_disabled(vcpu))
return false;
@@ -1242,7 +1242,7 @@ static bool access_pmswinc(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (pmu_write_swinc_el0_disabled(vcpu))
return false;
- mask = kvm_pmu_valid_counter_mask(vcpu);
+ mask = kvm_pmu_implemented_counter_mask(vcpu);
kvm_pmu_software_increment(vcpu, p->regval & mask);
return true;
}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 35d4ca4f6122..6af5ac1bdd54 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -47,7 +47,7 @@ static __always_inline bool kvm_arm_support_pmu_v3(void)
#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
-u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu);
+u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu);
u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1);
void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu);
void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu);
@@ -113,7 +113,7 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
}
static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
u64 select_idx, u64 val) {}
-static inline u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
+static inline u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
{
return 0;
}
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 02/12] KVM: arm64: nv: Adjust range of accessible PMCs according to HPMN
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
2024-08-27 0:22 ` [PATCH v2 01/12] KVM: arm64: Rename kvm_pmu_valid_counter_mask() Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 03/12] KVM: arm64: nv: Rename BEHAVE_FORWARD_ANY Oliver Upton
` (9 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
The value of MDCR_EL2.HPMN controls the number of event counters made
visible to EL0 and EL1. This means it is possible for the guest
hypervisor to allow direct access to event counters to the L2.
Rework KVM's PMU register emulation to take the effects of HPMN into
account when handling a trap. For bitmask-style registers, writes only
affect accessible registers.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/pmu-emul.c | 14 +++++++++++++-
arch/arm64/kvm/sys_regs.c | 12 ++++++------
include/kvm/arm_pmu.h | 5 +++++
3 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index e88d6bd8221a..f0081dc891a0 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -275,6 +275,18 @@ u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);
}
+u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu)
+{
+ u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
+ u64 hpmn;
+
+ if (!vcpu_has_nv(vcpu) || vcpu_is_el2(vcpu))
+ return mask;
+
+ hpmn = SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2));
+ return mask & GENMASK(hpmn - 1, 0);
+}
+
/**
* kvm_pmu_enable_counter_mask - enable selected PMU counters
* @vcpu: The vcpu pointer
@@ -574,7 +586,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0);
if (val & ARMV8_PMU_PMCR_P) {
- unsigned long mask = kvm_pmu_implemented_counter_mask(vcpu);
+ unsigned long mask = kvm_pmu_accessible_counter_mask(vcpu);
mask &= ~BIT(ARMV8_PMU_CYCLE_IDX);
for_each_set_bit(i, &mask, 32)
kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, i), 0, true);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index b25f6f44f451..b3c01d3be794 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1128,7 +1128,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va
{
bool set;
- val &= kvm_pmu_implemented_counter_mask(vcpu);
+ val &= kvm_pmu_accessible_counter_mask(vcpu);
switch (r->reg) {
case PMOVSSET_EL0:
@@ -1151,7 +1151,7 @@ static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 va
static int get_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 *val)
{
- u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
+ u64 mask = kvm_pmu_accessible_counter_mask(vcpu);
*val = __vcpu_sys_reg(vcpu, r->reg) & mask;
return 0;
@@ -1165,7 +1165,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (pmu_access_el0_disabled(vcpu))
return false;
- mask = kvm_pmu_implemented_counter_mask(vcpu);
+ mask = kvm_pmu_accessible_counter_mask(vcpu);
if (p->is_write) {
val = p->regval & mask;
if (r->Op2 & 0x1) {
@@ -1188,7 +1188,7 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
+ u64 mask = kvm_pmu_accessible_counter_mask(vcpu);
if (check_pmu_access_disabled(vcpu, 0))
return false;
@@ -1212,7 +1212,7 @@ static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static bool access_pmovs(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
+ u64 mask = kvm_pmu_accessible_counter_mask(vcpu);
if (pmu_access_el0_disabled(vcpu))
return false;
@@ -1242,7 +1242,7 @@ static bool access_pmswinc(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (pmu_write_swinc_el0_disabled(vcpu))
return false;
- mask = kvm_pmu_implemented_counter_mask(vcpu);
+ mask = kvm_pmu_accessible_counter_mask(vcpu);
kvm_pmu_software_increment(vcpu, p->regval & mask);
return true;
}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 6af5ac1bdd54..25017e72c562 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -48,6 +48,7 @@ static __always_inline bool kvm_arm_support_pmu_v3(void)
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu);
+u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu);
u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1);
void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu);
void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu);
@@ -117,6 +118,10 @@ static inline u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
{
return 0;
}
+static inline u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
static inline void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu) {}
static inline void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu) {}
static inline void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu) {}
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 03/12] KVM: arm64: nv: Rename BEHAVE_FORWARD_ANY
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
2024-08-27 0:22 ` [PATCH v2 01/12] KVM: arm64: Rename kvm_pmu_valid_counter_mask() Oliver Upton
2024-08-27 0:22 ` [PATCH v2 02/12] KVM: arm64: nv: Adjust range of accessible PMCs according to HPMN Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0 Oliver Upton
` (8 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
BEHAVE_FORWARD_ANY is slightly ambiguous, especially since we're about
to cram some more information into the enum. Rephrase it.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 83 +++++++++++++++++----------------
1 file changed, 42 insertions(+), 41 deletions(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 05166eccea0a..26d32f308dd3 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -16,9 +16,10 @@
enum trap_behaviour {
BEHAVE_HANDLE_LOCALLY = 0,
+
BEHAVE_FORWARD_READ = BIT(0),
BEHAVE_FORWARD_WRITE = BIT(1),
- BEHAVE_FORWARD_ANY = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE,
+ BEHAVE_FORWARD_RW = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE,
};
struct trap_bits {
@@ -130,7 +131,7 @@ static const struct trap_bits coarse_trap_bits[] = {
.index = HCR_EL2,
.value = HCR_TID2,
.mask = HCR_TID2,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TID3] = {
.index = HCR_EL2,
@@ -154,37 +155,37 @@ static const struct trap_bits coarse_trap_bits[] = {
.index = HCR_EL2,
.value = HCR_TIDCP,
.mask = HCR_TIDCP,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TACR] = {
.index = HCR_EL2,
.value = HCR_TACR,
.mask = HCR_TACR,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TSW] = {
.index = HCR_EL2,
.value = HCR_TSW,
.mask = HCR_TSW,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TPC] = { /* Also called TCPC when FEAT_DPB is implemented */
.index = HCR_EL2,
.value = HCR_TPC,
.mask = HCR_TPC,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TPU] = {
.index = HCR_EL2,
.value = HCR_TPU,
.mask = HCR_TPU,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TTLB] = {
.index = HCR_EL2,
.value = HCR_TTLB,
.mask = HCR_TTLB,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TVM] = {
.index = HCR_EL2,
@@ -196,7 +197,7 @@ static const struct trap_bits coarse_trap_bits[] = {
.index = HCR_EL2,
.value = HCR_TDZ,
.mask = HCR_TDZ,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TRVM] = {
.index = HCR_EL2,
@@ -208,175 +209,175 @@ static const struct trap_bits coarse_trap_bits[] = {
.index = HCR_EL2,
.value = HCR_TLOR,
.mask = HCR_TLOR,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TERR] = {
.index = HCR_EL2,
.value = HCR_TERR,
.mask = HCR_TERR,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_APK] = {
.index = HCR_EL2,
.value = 0,
.mask = HCR_APK,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_NV] = {
.index = HCR_EL2,
.value = HCR_NV,
.mask = HCR_NV,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_NV_nNV2] = {
.index = HCR_EL2,
.value = HCR_NV,
.mask = HCR_NV | HCR_NV2,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_NV1_nNV2] = {
.index = HCR_EL2,
.value = HCR_NV | HCR_NV1,
.mask = HCR_NV | HCR_NV1 | HCR_NV2,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_AT] = {
.index = HCR_EL2,
.value = HCR_AT,
.mask = HCR_AT,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_nFIEN] = {
.index = HCR_EL2,
.value = 0,
.mask = HCR_FIEN,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TID4] = {
.index = HCR_EL2,
.value = HCR_TID4,
.mask = HCR_TID4,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TICAB] = {
.index = HCR_EL2,
.value = HCR_TICAB,
.mask = HCR_TICAB,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TOCU] = {
.index = HCR_EL2,
.value = HCR_TOCU,
.mask = HCR_TOCU,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_ENSCXT] = {
.index = HCR_EL2,
.value = 0,
.mask = HCR_ENSCXT,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TTLBIS] = {
.index = HCR_EL2,
.value = HCR_TTLBIS,
.mask = HCR_TTLBIS,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCR_TTLBOS] = {
.index = HCR_EL2,
.value = HCR_TTLBOS,
.mask = HCR_TTLBOS,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TPMCR] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TPMCR,
.mask = MDCR_EL2_TPMCR,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TPM] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TPM,
.mask = MDCR_EL2_TPM,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TDE] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TDE,
.mask = MDCR_EL2_TDE,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TDA] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TDA,
.mask = MDCR_EL2_TDA,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TDOSA] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TDOSA,
.mask = MDCR_EL2_TDOSA,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TDRA] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TDRA,
.mask = MDCR_EL2_TDRA,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_E2PB] = {
.index = MDCR_EL2,
.value = 0,
.mask = BIT(MDCR_EL2_E2PB_SHIFT),
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TPMS] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TPMS,
.mask = MDCR_EL2_TPMS,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TTRF] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TTRF,
.mask = MDCR_EL2_TTRF,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_E2TB] = {
.index = MDCR_EL2,
.value = 0,
.mask = BIT(MDCR_EL2_E2TB_SHIFT),
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_MDCR_TDCC] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TDCC,
.mask = MDCR_EL2_TDCC,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_CPACR_E0POE] = {
.index = CPTR_EL2,
.value = CPACR_ELx_E0POE,
.mask = CPACR_ELx_E0POE,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_CPTR_TAM] = {
.index = CPTR_EL2,
.value = CPTR_EL2_TAM,
.mask = CPTR_EL2_TAM,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_CPTR_TCPAC] = {
.index = CPTR_EL2,
.value = CPTR_EL2_TCPAC,
.mask = CPTR_EL2_TCPAC,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
[CGT_HCRX_TCR2En] = {
.index = HCRX_EL2,
.value = 0,
.mask = HCRX_EL2_TCR2En,
- .behaviour = BEHAVE_FORWARD_ANY,
+ .behaviour = BEHAVE_FORWARD_RW,
},
};
@@ -434,7 +435,7 @@ static enum trap_behaviour check_cnthctl_el1pcten(struct kvm_vcpu *vcpu)
if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCTEN << 10))
return BEHAVE_HANDLE_LOCALLY;
- return BEHAVE_FORWARD_ANY;
+ return BEHAVE_FORWARD_RW;
}
static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu)
@@ -442,7 +443,7 @@ static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu)
if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCEN << 10))
return BEHAVE_HANDLE_LOCALLY;
- return BEHAVE_FORWARD_ANY;
+ return BEHAVE_FORWARD_RW;
}
static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu)
@@ -453,7 +454,7 @@ static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu)
val = translate_cptr_el2_to_cpacr_el1(val);
if (val & CPACR_ELx_TTA)
- return BEHAVE_FORWARD_ANY;
+ return BEHAVE_FORWARD_RW;
return BEHAVE_HANDLE_LOCALLY;
}
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (2 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 03/12] KVM: arm64: nv: Rename BEHAVE_FORWARD_ANY Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 16:24 ` Marc Zyngier
2024-08-27 0:22 ` [PATCH v2 05/12] KVM: arm64: nv: Reinject traps that take effect in " Oliver Upton
` (7 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
The EL2 PMU trap controls are rather annoying, because they take effect
for *host* EL0 in addition to the guest. Set up some basic support for
describing EL2 traps that affect host EL0.
Do so by describing trap bits that can take effect in host EL0. Preserve
the early return for most hyp context traps by taking a bit out of the
trap_config value to indicate if the trap might take effect InHost.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 37 ++++++++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 26d32f308dd3..b9f3e19a1b96 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -20,6 +20,9 @@ enum trap_behaviour {
BEHAVE_FORWARD_READ = BIT(0),
BEHAVE_FORWARD_WRITE = BIT(1),
BEHAVE_FORWARD_RW = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE,
+
+ /* Traps that take effect in Host EL0, this is rare! */
+ BEHAVE_IN_HOST_EL0 = BIT(2),
};
struct trap_bits {
@@ -478,7 +481,8 @@ static const complex_condition_check ccc[] = {
* [20] trap polarity (1 bit)
* [25:21] FG filter (5 bits)
* [35:26] Main SysReg table index (10 bits)
- * [62:36] Unused (27 bits)
+ * [36] Trap applies to Host EL0 (1 bit)
+ * [62:37] Unused (26 bits)
* [63] RES0 - Must be zero, as lost on insertion in the xarray
*/
#define TC_CGT_BITS 10
@@ -495,7 +499,8 @@ union trap_config {
unsigned long pol:1; /* Polarity */
unsigned long fgf:TC_FGF_BITS; /* Fine Grained Filter */
unsigned long sri:TC_SRI_BITS; /* SysReg Index */
- unsigned long unused:27; /* Unused, should be zero */
+ unsigned long in_host_el0:1; /* Applies to Host EL0 */
+ unsigned long unused:26; /* Unused, should be zero */
unsigned long mbz:1; /* Must Be Zero */
};
};
@@ -1875,6 +1880,28 @@ static u32 encoding_next(u32 encoding)
return sys_reg(op0 + 1, 0, 0, 0, 0);
}
+static bool trap_effective_in_host_el0(const enum cgt_group_id id)
+{
+ switch (id) {
+ case __RESERVED__ ... __MULTIPLE_CONTROL_BITS__ - 1:
+ return coarse_trap_bits[id].behaviour & BEHAVE_IN_HOST_EL0;
+ case __MULTIPLE_CONTROL_BITS__ ... __COMPLEX_CONDITIONS__ - 1: {
+ const enum cgt_group_id *cgids;
+ int i;
+
+ cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__];
+ for (i = 0; cgids[i] != __RESERVED__; i++)
+ if (trap_effective_in_host_el0(cgids[i]))
+ return true;
+
+ return false;
+ }
+ /* Just treat complex traps as InHost for now. */
+ default:
+ return true;
+ }
+}
+
int __init populate_nv_trap_config(void)
{
int ret = 0;
@@ -1886,13 +1913,17 @@ int __init populate_nv_trap_config(void)
for (int i = 0; i < ARRAY_SIZE(encoding_to_cgt); i++) {
const struct encoding_to_trap_config *cgt = &encoding_to_cgt[i];
+ union trap_config tc = cgt->tc;
void *prev;
- if (cgt->tc.val & BIT(63)) {
+ if (tc.val & BIT(63)) {
kvm_err("CGT[%d] has MBZ bit set\n", i);
ret = -EINVAL;
}
+ if (trap_effective_in_host_el0(tc.cgt))
+ tc.in_host_el0 = true;
+
for (u32 enc = cgt->encoding; enc <= cgt->end; enc = encoding_next(enc)) {
prev = xa_store(&sr_forward_xa, enc,
xa_mk_value(cgt->tc.val), GFP_KERNEL);
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 05/12] KVM: arm64: nv: Reinject traps that take effect in Host EL0
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (3 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0 Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 06/12] KVM: arm64: nv: Honor MDCR_EL2.{TPM, TPMCR} " Oliver Upton
` (6 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Wire up the other end of traps that affect host EL0 by actually
injecting them into the guest hypervisor. Skip over FGT entirely, as a
cursory glance suggests no FGT is effective in host EL0.
Note that kvm_inject_nested() is already equipped for handling
exceptions while the VM is already in a host context.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/include/asm/kvm_emulate.h | 5 +++++
arch/arm64/kvm/emulate-nested.c | 25 +++++++++++++++++++++----
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index a601a9305b10..bf0c48403f59 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -225,6 +225,11 @@ static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu)
return vcpu_has_nv(vcpu) && __is_hyp_ctxt(&vcpu->arch.ctxt);
}
+static inline bool vcpu_is_host_el0(const struct kvm_vcpu *vcpu)
+{
+ return is_hyp_ctxt(vcpu) && !vcpu_is_el2(vcpu);
+}
+
/*
* The layout of SPSR for an AArch32 state is different when observed from an
* AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index b9f3e19a1b96..4548725a0899 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2087,11 +2087,19 @@ static u64 kvm_get_sysreg_res0(struct kvm *kvm, enum vcpu_sysreg sr)
return masks->mask[sr - __VNCR_START__].res0;
}
-static bool check_fgt_bit(struct kvm *kvm, bool is_read,
+static bool check_fgt_bit(struct kvm_vcpu *vcpu, bool is_read,
u64 val, const union trap_config tc)
{
+ struct kvm *kvm = vcpu->kvm;
enum vcpu_sysreg sr;
+ /*
+ * KVM doesn't know about any FGTs that apply to the host, and hopefully
+ * that'll remain the case.
+ */
+ if (is_hyp_ctxt(vcpu))
+ return false;
+
if (tc.pol)
return (val & BIT(tc.bit));
@@ -2168,7 +2176,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index)
* If we're not nesting, immediately return to the caller, with the
* sysreg index, should we have it.
*/
- if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
+ if (!vcpu_has_nv(vcpu))
+ goto local;
+
+ /*
+ * Handle the trap locally if we're in a hyp context where the trap
+ * config does not apply.
+ */
+ if (is_hyp_ctxt(vcpu) && !(vcpu_is_host_el0(vcpu) && tc.in_host_el0))
goto local;
switch ((enum fgt_group_id)tc.fgt) {
@@ -2214,12 +2229,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index)
goto local;
}
- if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(vcpu->kvm, is_read,
- val, tc))
+ if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(vcpu, is_read, val, tc))
goto inject;
b = compute_trap_behaviour(vcpu, tc);
+ if (!(b & BEHAVE_IN_HOST_EL0) && vcpu_is_host_el0(vcpu))
+ goto local;
+
if (((b & BEHAVE_FORWARD_READ) && is_read) ||
((b & BEHAVE_FORWARD_WRITE) && !is_read))
goto inject;
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 06/12] KVM: arm64: nv: Honor MDCR_EL2.{TPM, TPMCR} in Host EL0
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (4 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 05/12] KVM: arm64: nv: Reinject traps that take effect in " Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 07/12] KVM: arm64: nv: Allow coarse-grained trap combos to use complex traps Oliver Upton
` (5 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
TPM and TPMCR trap bits also affect Host EL0. How fun.
Mark these two trap bits as such and take advantage of the new
infrastructure for dealing w/ EL0 traps.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 4548725a0899..41839ad85bcf 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -296,13 +296,15 @@ static const struct trap_bits coarse_trap_bits[] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TPMCR,
.mask = MDCR_EL2_TPMCR,
- .behaviour = BEHAVE_FORWARD_RW,
+ .behaviour = BEHAVE_FORWARD_RW |
+ BEHAVE_IN_HOST_EL0,
},
[CGT_MDCR_TPM] = {
.index = MDCR_EL2,
.value = MDCR_EL2_TPM,
.mask = MDCR_EL2_TPM,
- .behaviour = BEHAVE_FORWARD_RW,
+ .behaviour = BEHAVE_FORWARD_RW |
+ BEHAVE_IN_HOST_EL0,
},
[CGT_MDCR_TDE] = {
.index = MDCR_EL2,
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 07/12] KVM: arm64: nv: Allow coarse-grained trap combos to use complex traps
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (5 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 06/12] KVM: arm64: nv: Honor MDCR_EL2.{TPM, TPMCR} " Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 08/12] KVM: arm64: nv: Describe trap behaviour of MDCR_EL2.HPMN Oliver Upton
` (4 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
KVM uses a sanity-check to avoid infinite recursion in trap combinations
that could potentially depend on itself. Narrow the scope of this sanity
check to the exact CGT IDs that correspond w/ trap combos, opening the
door to using 'complex' traps as part of a combination.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 41839ad85bcf..afc1e2a0e308 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -1984,7 +1984,8 @@ int __init populate_nv_trap_config(void)
cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__];
for (int i = 0; cgids[i] != __RESERVED__; i++) {
- if (cgids[i] >= __MULTIPLE_CONTROL_BITS__) {
+ if (cgids[i] >= __MULTIPLE_CONTROL_BITS__ &&
+ cgids[i] < __COMPLEX_CONDITIONS__) {
kvm_err("Recursive MCB %d/%d\n", id, cgids[i]);
ret = -EINVAL;
}
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 08/12] KVM: arm64: nv: Describe trap behaviour of MDCR_EL2.HPMN
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (6 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 07/12] KVM: arm64: nv: Allow coarse-grained trap combos to use complex traps Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0 Oliver Upton
` (3 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
MDCR_EL2.HPMN splits the PMU event counters into two ranges: the first
range is accessible from all ELs, and the second range is accessible
only to EL2/3. Supposing the guest hypervisor allows direct access to
the PMU counters from the L2, KVM needs to locally handle those
accesses.
Add a new complex trap configuration for HPMN that checks if the counter
index is accessible to the current context. As written, the architecture
suggests HPMN only causes PMEVCNTR<n>_EL0 to trap, though intuition (and
the pseudocode) suggest that the trap applies to PMEVTYPER<n>_EL0 as
well.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 165 +++++++++++++++++++-------------
1 file changed, 101 insertions(+), 64 deletions(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index afc1e2a0e308..ae183dea79b7 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -104,6 +104,7 @@ enum cgt_group_id {
CGT_HCR_TPU_TOCU,
CGT_HCR_NV1_nNV2_ENSCXT,
CGT_MDCR_TPM_TPMCR,
+ CGT_MDCR_TPM_HPMN,
CGT_MDCR_TDE_TDA,
CGT_MDCR_TDE_TDOSA,
CGT_MDCR_TDE_TDRA,
@@ -118,6 +119,7 @@ enum cgt_group_id {
CGT_CNTHCTL_EL1PTEN,
CGT_CPTR_TTA,
+ CGT_MDCR_HPMN,
/* Must be last */
__NR_CGT_GROUP_IDS__
@@ -404,6 +406,7 @@ static const enum cgt_group_id *coarse_control_combo[] = {
MCB(CGT_HCR_TPU_TOCU, CGT_HCR_TPU, CGT_HCR_TOCU),
MCB(CGT_HCR_NV1_nNV2_ENSCXT, CGT_HCR_NV1_nNV2, CGT_HCR_ENSCXT),
MCB(CGT_MDCR_TPM_TPMCR, CGT_MDCR_TPM, CGT_MDCR_TPMCR),
+ MCB(CGT_MDCR_TPM_HPMN, CGT_MDCR_TPM, CGT_MDCR_HPMN),
MCB(CGT_MDCR_TDE_TDA, CGT_MDCR_TDE, CGT_MDCR_TDA),
MCB(CGT_MDCR_TDE_TDOSA, CGT_MDCR_TDE, CGT_MDCR_TDOSA),
MCB(CGT_MDCR_TDE_TDRA, CGT_MDCR_TDE, CGT_MDCR_TDRA),
@@ -464,6 +467,39 @@ static enum trap_behaviour check_cptr_tta(struct kvm_vcpu *vcpu)
return BEHAVE_HANDLE_LOCALLY;
}
+static enum trap_behaviour check_mdcr_hpmn(struct kvm_vcpu *vcpu)
+{
+ u32 sysreg = esr_sys64_to_sysreg(kvm_vcpu_get_esr(vcpu));
+ u64 mask = kvm_pmu_accessible_counter_mask(vcpu);
+ unsigned int idx;
+
+
+ switch (sysreg) {
+ case SYS_PMEVTYPERn_EL0(0) ... SYS_PMEVTYPERn_EL0(30):
+ case SYS_PMEVCNTRn_EL0(0) ... SYS_PMEVCNTRn_EL0(30):
+ idx = (sys_reg_CRm(sysreg) & 0x3) << 3 | sys_reg_Op2(sysreg);
+ break;
+ case SYS_PMXEVTYPER_EL0:
+ case SYS_PMXEVCNTR_EL0:
+ idx = __vcpu_sys_reg(vcpu, PMSELR_EL0) & ARMV8_PMU_COUNTER_MASK;
+ break;
+ default:
+ /* Someone used this trap helper for something else... */
+ KVM_BUG_ON(1, vcpu->kvm);
+ return BEHAVE_HANDLE_LOCALLY;
+ }
+
+ /*
+ * Programming HPMN=0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't
+ * implemented. Since KVM's ability to emulate HPMN=0 does not directly
+ * depend on hardware (all PMU registers are trapped), make the
+ * implementation choice that all counters are included in the second
+ * range reserved for EL2/EL3.
+ */
+ return !(BIT(idx) & mask) ? (BEHAVE_FORWARD_RW | BEHAVE_IN_HOST_EL0) :
+ BEHAVE_HANDLE_LOCALLY;
+}
+
#define CCC(id, fn) \
[id - __COMPLEX_CONDITIONS__] = fn
@@ -471,6 +507,7 @@ static const complex_condition_check ccc[] = {
CCC(CGT_CNTHCTL_EL1PCTEN, check_cnthctl_el1pcten),
CCC(CGT_CNTHCTL_EL1PTEN, check_cnthctl_el1pten),
CCC(CGT_CPTR_TTA, check_cptr_tta),
+ CCC(CGT_MDCR_HPMN, check_mdcr_hpmn),
};
/*
@@ -885,77 +922,77 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {
SR_TRAP(SYS_PMOVSCLR_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_PMCEID0_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_PMCEID1_EL0, CGT_MDCR_TPM),
- SR_TRAP(SYS_PMXEVTYPER_EL0, CGT_MDCR_TPM),
+ SR_TRAP(SYS_PMXEVTYPER_EL0, CGT_MDCR_TPM_HPMN),
SR_TRAP(SYS_PMSWINC_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_PMSELR_EL0, CGT_MDCR_TPM),
- SR_TRAP(SYS_PMXEVCNTR_EL0, CGT_MDCR_TPM),
+ SR_TRAP(SYS_PMXEVCNTR_EL0, CGT_MDCR_TPM_HPMN),
SR_TRAP(SYS_PMCCNTR_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_PMUSERENR_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_PMINTENSET_EL1, CGT_MDCR_TPM),
SR_TRAP(SYS_PMINTENCLR_EL1, CGT_MDCR_TPM),
SR_TRAP(SYS_PMMIR_EL1, CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(0), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(1), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(2), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(3), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(4), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(5), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(6), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(7), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(8), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(9), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(10), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(11), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(12), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(13), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(14), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(15), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(16), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(17), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(18), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(19), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(20), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(21), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(22), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(23), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(24), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(25), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(26), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(27), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(28), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(29), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVCNTRn_EL0(30), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(0), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(1), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(2), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(3), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(4), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(5), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(6), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(7), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(8), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(9), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(10), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(11), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(12), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(13), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(14), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(15), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(16), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(17), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(18), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(19), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(20), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(21), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(22), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(23), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(24), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(25), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(26), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(27), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(28), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(29), CGT_MDCR_TPM),
- SR_TRAP(SYS_PMEVTYPERn_EL0(30), CGT_MDCR_TPM),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(0), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(1), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(2), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(3), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(4), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(5), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(6), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(7), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(8), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(9), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(10), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(11), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(12), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(13), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(14), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(15), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(16), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(17), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(18), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(19), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(20), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(21), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(22), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(23), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(24), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(25), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(26), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(27), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(28), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(29), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVCNTRn_EL0(30), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(0), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(1), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(2), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(3), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(4), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(5), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(6), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(7), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(8), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(9), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(10), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(11), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(12), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(13), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(14), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(15), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(16), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(17), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(18), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(19), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(20), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(21), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(22), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(23), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(24), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(25), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(26), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(27), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(28), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(29), CGT_MDCR_TPM_HPMN),
+ SR_TRAP(SYS_PMEVTYPERn_EL0(30), CGT_MDCR_TPM_HPMN),
SR_TRAP(SYS_PMCCFILTR_EL0, CGT_MDCR_TPM),
SR_TRAP(SYS_MDCCSR_EL0, CGT_MDCR_TDCC_TDE_TDA),
SR_TRAP(SYS_MDCCINT_EL1, CGT_MDCR_TDCC_TDE_TDA),
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (7 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 08/12] KVM: arm64: nv: Describe trap behaviour of MDCR_EL2.HPMN Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 16:40 ` Marc Zyngier
2024-08-27 0:22 ` [PATCH v2 10/12] KVM: arm64: Add helpers to determine if PMC counts at a given EL Oliver Upton
` (2 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Everything is in place now for KVM to actually handle MDCR_EL2.HPMN. Not
only that, the emulation is capable of doing FEAT_HPMN0. Advertise
support for the feature in the VM's ID registers.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/nested.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index de789e0f1ae9..206aff3d88f7 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -910,12 +910,13 @@ static void limit_nv_id_regs(struct kvm *kvm)
ID_AA64MMFR4_EL1_E2H0_NI_NV1);
kvm_set_vm_id_reg(kvm, SYS_ID_AA64MMFR4_EL1, val);
- /* Only limited support for PMU, Debug, BPs and WPs */
+ /* Only limited support for PMU, Debug, BPs, WPs, and HPMN0 */
val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1);
val &= (NV_FTR(DFR0, PMUVer) |
NV_FTR(DFR0, WRPs) |
NV_FTR(DFR0, BRPs) |
- NV_FTR(DFR0, DebugVer));
+ NV_FTR(DFR0, DebugVer) |
+ NV_FTR(DFR0, HPMN0));
/* Cap Debug to ARMv8.1 */
tmp = FIELD_GET(NV_FTR(DFR0, DebugVer), val);
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 10/12] KVM: arm64: Add helpers to determine if PMC counts at a given EL
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (8 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0 Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 11/12] KVM: arm64: nv: Honor NSH filter when in hyp context Oliver Upton
2024-08-27 0:22 ` [PATCH v2 12/12] KVM: arm64: nv: Reprogram PMU events affected by nested transition Oliver Upton
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Checking the exception level filters for a PMC is a minor annoyance to
open code. Add helpers to check if an event counts at EL0 and EL1, which
will prove useful in a subsequent change.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/pmu-emul.c | 40 +++++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index f0081dc891a0..fbefad8913bf 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -111,6 +111,11 @@ static u32 counter_index_to_evtreg(u64 idx)
return (idx == ARMV8_PMU_CYCLE_IDX) ? PMCCFILTR_EL0 : PMEVTYPER0_EL0 + idx;
}
+static u64 kvm_pmc_read_evtreg(const struct kvm_pmc *pmc)
+{
+ return __vcpu_sys_reg(kvm_pmc_to_vcpu(pmc), counter_index_to_evtreg(pmc->idx));
+}
+
static u64 kvm_pmu_get_pmc_value(struct kvm_pmc *pmc)
{
struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
@@ -601,6 +606,24 @@ static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)
(__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx));
}
+static bool kvm_pmc_counts_at_el0(struct kvm_pmc *pmc)
+{
+ u64 evtreg = kvm_pmc_read_evtreg(pmc);
+ bool nsu = evtreg & ARMV8_PMU_EXCLUDE_NS_EL0;
+ bool u = evtreg & ARMV8_PMU_EXCLUDE_EL0;
+
+ return u == nsu;
+}
+
+static bool kvm_pmc_counts_at_el1(struct kvm_pmc *pmc)
+{
+ u64 evtreg = kvm_pmc_read_evtreg(pmc);
+ bool nsk = evtreg & ARMV8_PMU_EXCLUDE_NS_EL1;
+ bool p = evtreg & ARMV8_PMU_EXCLUDE_EL1;
+
+ return p == nsk;
+}
+
/**
* kvm_pmu_create_perf_event - create a perf event for a counter
* @pmc: Counter context
@@ -611,17 +634,15 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
struct arm_pmu *arm_pmu = vcpu->kvm->arch.arm_pmu;
struct perf_event *event;
struct perf_event_attr attr;
- u64 eventsel, reg, data;
- bool p, u, nsk, nsu;
+ u64 eventsel, evtreg;
- reg = counter_index_to_evtreg(pmc->idx);
- data = __vcpu_sys_reg(vcpu, reg);
+ evtreg = kvm_pmc_read_evtreg(pmc);
kvm_pmu_stop_counter(pmc);
if (pmc->idx == ARMV8_PMU_CYCLE_IDX)
eventsel = ARMV8_PMUV3_PERFCTR_CPU_CYCLES;
else
- eventsel = data & kvm_pmu_event_mask(vcpu->kvm);
+ eventsel = evtreg & kvm_pmu_event_mask(vcpu->kvm);
/*
* Neither SW increment nor chained events need to be backed
@@ -639,18 +660,13 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
!test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
return;
- p = data & ARMV8_PMU_EXCLUDE_EL1;
- u = data & ARMV8_PMU_EXCLUDE_EL0;
- nsk = data & ARMV8_PMU_EXCLUDE_NS_EL1;
- nsu = data & ARMV8_PMU_EXCLUDE_NS_EL0;
-
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.type = arm_pmu->pmu.type;
attr.size = sizeof(attr);
attr.pinned = 1;
attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
- attr.exclude_user = (u != nsu);
- attr.exclude_kernel = (p != nsk);
+ attr.exclude_user = !kvm_pmc_counts_at_el0(pmc);
+ attr.exclude_kernel = !kvm_pmc_counts_at_el1(pmc);
attr.exclude_hv = 1; /* Don't count EL2 events */
attr.exclude_host = 1; /* Don't count host events */
attr.config = eventsel;
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 11/12] KVM: arm64: nv: Honor NSH filter when in hyp context
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (9 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 10/12] KVM: arm64: Add helpers to determine if PMC counts at a given EL Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
2024-08-27 0:22 ` [PATCH v2 12/12] KVM: arm64: nv: Reprogram PMU events affected by nested transition Oliver Upton
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
It hopefully comes as no surprise when I say that vEL2 actually runs at
EL1. So, the guest hypervisor's EL2 event filter (NSH) needs to actually
be applied to EL1 in the perf event.
This isn't quite enough yet, as the backing perf events need to be
reprogrammed upon nested ERET/exception entry to remap the effective
filter onto ::exclude_kernel.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/pmu-emul.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index fbefad8913bf..3dee669c0674 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -624,6 +624,11 @@ static bool kvm_pmc_counts_at_el1(struct kvm_pmc *pmc)
return p == nsk;
}
+static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc)
+{
+ return kvm_pmc_read_evtreg(pmc) & ARMV8_PMU_INCLUDE_EL2;
+}
+
/**
* kvm_pmu_create_perf_event - create a perf event for a counter
* @pmc: Counter context
@@ -666,11 +671,19 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
attr.pinned = 1;
attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
attr.exclude_user = !kvm_pmc_counts_at_el0(pmc);
- attr.exclude_kernel = !kvm_pmc_counts_at_el1(pmc);
attr.exclude_hv = 1; /* Don't count EL2 events */
attr.exclude_host = 1; /* Don't count host events */
attr.config = eventsel;
+ /*
+ * Filter events at EL1 (i.e. vEL2) when in a hyp context based on the
+ * guest's EL2 filter.
+ */
+ if (unlikely(is_hyp_ctxt(vcpu)))
+ attr.exclude_kernel = !kvm_pmc_counts_at_el2(pmc);
+ else
+ attr.exclude_kernel = !kvm_pmc_counts_at_el1(pmc);
+
/*
* If counting with a 64bit counter, advertise it to the perf
* code, carefully dealing with the initial sample period
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 12/12] KVM: arm64: nv: Reprogram PMU events affected by nested transition
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
` (10 preceding siblings ...)
2024-08-27 0:22 ` [PATCH v2 11/12] KVM: arm64: nv: Honor NSH filter when in hyp context Oliver Upton
@ 2024-08-27 0:22 ` Oliver Upton
11 siblings, 0 replies; 15+ messages in thread
From: Oliver Upton @ 2024-08-27 0:22 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni, Oliver Upton
Start reprogramming PMU events at nested boundaries now that everything
is in place to handle the EL2 event filter. Only repaint events where
the filter differs between EL1 and EL2 as a slight optimization.
PMU now 'works' for nested VMs, albeit slow.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/emulate-nested.c | 4 ++++
arch/arm64/kvm/pmu-emul.c | 24 ++++++++++++++++++++++++
include/kvm/arm_pmu.h | 3 +++
3 files changed, 31 insertions(+)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index ae183dea79b7..6f748c4257f5 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2411,6 +2411,8 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
kvm_arch_vcpu_load(vcpu, smp_processor_id());
preempt_enable();
+
+ kvm_pmu_reprogram_events(vcpu);
}
static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2,
@@ -2493,6 +2495,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2,
kvm_arch_vcpu_load(vcpu, smp_processor_id());
preempt_enable();
+ kvm_pmu_reprogram_events(vcpu);
+
return 1;
}
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 3dee669c0674..0cd0426892be 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -1180,3 +1180,27 @@ u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu)
return u64_replace_bits(pmcr, vcpu->kvm->arch.pmcr_n, ARMV8_PMU_PMCR_N);
}
+
+void kvm_pmu_reprogram_events(struct kvm_vcpu *vcpu)
+{
+ unsigned long mask;
+ int i;
+
+ if (!kvm_vcpu_has_pmu(vcpu))
+ return;
+
+ mask = __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
+ for_each_set_bit(i, &mask, 32) {
+ struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, i);
+
+ /*
+ * We only need to reconfigure events where the filter is
+ * different at EL1 vs. EL2, as we're multiplexing the true EL1
+ * event filter bit for nested.
+ */
+ if (kvm_pmc_counts_at_el1(pmc) == kvm_pmc_counts_at_el2(pmc))
+ continue;
+
+ kvm_pmu_create_perf_event(pmc);
+ }
+}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 25017e72c562..4e3f35b00bf8 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -97,6 +97,7 @@ int kvm_arm_set_default_pmu(struct kvm *kvm);
u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm);
u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu);
+void kvm_pmu_reprogram_events(struct kvm_vcpu *vcpu);
#else
struct kvm_pmu {
};
@@ -192,6 +193,8 @@ static inline u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu)
return 0;
}
+static inline void kvm_pmu_reprogram_events(struct kvm_vcpu *vcpu) {}
+
#endif
#endif
--
2.46.0.295.g3b9ea8a38a-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0
2024-08-27 0:22 ` [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0 Oliver Upton
@ 2024-08-27 16:24 ` Marc Zyngier
0 siblings, 0 replies; 15+ messages in thread
From: Marc Zyngier @ 2024-08-27 16:24 UTC (permalink / raw)
To: Oliver Upton
Cc: kvmarm, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni
On Tue, 27 Aug 2024 01:22:27 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
>
> The EL2 PMU trap controls are rather annoying, because they take effect
> for *host* EL0 in addition to the guest. Set up some basic support for
> describing EL2 traps that affect host EL0.
>
> Do so by describing trap bits that can take effect in host EL0. Preserve
> the early return for most hyp context traps by taking a bit out of the
> trap_config value to indicate if the trap might take effect InHost.
>
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
> arch/arm64/kvm/emulate-nested.c | 37 ++++++++++++++++++++++++++++++---
> 1 file changed, 34 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
> index 26d32f308dd3..b9f3e19a1b96 100644
> --- a/arch/arm64/kvm/emulate-nested.c
> +++ b/arch/arm64/kvm/emulate-nested.c
> @@ -20,6 +20,9 @@ enum trap_behaviour {
> BEHAVE_FORWARD_READ = BIT(0),
> BEHAVE_FORWARD_WRITE = BIT(1),
> BEHAVE_FORWARD_RW = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE,
> +
> + /* Traps that take effect in Host EL0, this is rare! */
> + BEHAVE_IN_HOST_EL0 = BIT(2),
Do we need to distinguish reads from writes here? There are precedents
with things like TVM/TRVM, though not affecting EL0.
> };
>
> struct trap_bits {
> @@ -478,7 +481,8 @@ static const complex_condition_check ccc[] = {
> * [20] trap polarity (1 bit)
> * [25:21] FG filter (5 bits)
> * [35:26] Main SysReg table index (10 bits)
> - * [62:36] Unused (27 bits)
> + * [36] Trap applies to Host EL0 (1 bit)
> + * [62:37] Unused (26 bits)
> * [63] RES0 - Must be zero, as lost on insertion in the xarray
> */
> #define TC_CGT_BITS 10
> @@ -495,7 +499,8 @@ union trap_config {
> unsigned long pol:1; /* Polarity */
> unsigned long fgf:TC_FGF_BITS; /* Fine Grained Filter */
> unsigned long sri:TC_SRI_BITS; /* SysReg Index */
> - unsigned long unused:27; /* Unused, should be zero */
> + unsigned long in_host_el0:1; /* Applies to Host EL0 */
I'm a bit confused about the meaning of this new bit. If something
applies to a Host EL0 access, we already have the information as part
of the coarse grained traps. Why do we need something extra?
I understand that it is used as some form of summary information and
avoids looking at the CGT chain, but this also muddies the purpose of
the trap_config word, because this doesn't describe *why* we have
trapped.
It's not really a big deal, as we have plenty of spare bits so far,
but I find it a bit jarring.
> + unsigned long unused:26; /* Unused, should be zero */
> unsigned long mbz:1; /* Must Be Zero */
> };
> };
> @@ -1875,6 +1880,28 @@ static u32 encoding_next(u32 encoding)
> return sys_reg(op0 + 1, 0, 0, 0, 0);
> }
>
> +static bool trap_effective_in_host_el0(const enum cgt_group_id id)
> +{
> + switch (id) {
> + case __RESERVED__ ... __MULTIPLE_CONTROL_BITS__ - 1:
> + return coarse_trap_bits[id].behaviour & BEHAVE_IN_HOST_EL0;
> + case __MULTIPLE_CONTROL_BITS__ ... __COMPLEX_CONDITIONS__ - 1: {
> + const enum cgt_group_id *cgids;
> + int i;
> +
> + cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__];
> + for (i = 0; cgids[i] != __RESERVED__; i++)
> + if (trap_effective_in_host_el0(cgids[i]))
> + return true;
> +
> + return false;
> + }
> + /* Just treat complex traps as InHost for now. */
s/InHost/InHost-capable/
> + default:
> + return true;
> + }
> +}
> +
> int __init populate_nv_trap_config(void)
> {
> int ret = 0;
> @@ -1886,13 +1913,17 @@ int __init populate_nv_trap_config(void)
>
> for (int i = 0; i < ARRAY_SIZE(encoding_to_cgt); i++) {
> const struct encoding_to_trap_config *cgt = &encoding_to_cgt[i];
> + union trap_config tc = cgt->tc;
> void *prev;
>
> - if (cgt->tc.val & BIT(63)) {
> + if (tc.val & BIT(63)) {
> kvm_err("CGT[%d] has MBZ bit set\n", i);
> ret = -EINVAL;
> }
>
> + if (trap_effective_in_host_el0(tc.cgt))
> + tc.in_host_el0 = true;
nit: do a direct assignment without the test.
> +
> for (u32 enc = cgt->encoding; enc <= cgt->end; enc = encoding_next(enc)) {
> prev = xa_store(&sr_forward_xa, enc,
> xa_mk_value(cgt->tc.val), GFP_KERNEL);
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0
2024-08-27 0:22 ` [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0 Oliver Upton
@ 2024-08-27 16:40 ` Marc Zyngier
0 siblings, 0 replies; 15+ messages in thread
From: Marc Zyngier @ 2024-08-27 16:40 UTC (permalink / raw)
To: Oliver Upton
Cc: kvmarm, James Morse, Suzuki K Poulose, Zenghui Yu,
Ganapatrao Kulkarni
On Tue, 27 Aug 2024 01:22:32 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
>
> Everything is in place now for KVM to actually handle MDCR_EL2.HPMN. Not
> only that, the emulation is capable of doing FEAT_HPMN0. Advertise
> support for the feature in the VM's ID registers.
>
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
> arch/arm64/kvm/nested.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index de789e0f1ae9..206aff3d88f7 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -910,12 +910,13 @@ static void limit_nv_id_regs(struct kvm *kvm)
> ID_AA64MMFR4_EL1_E2H0_NI_NV1);
> kvm_set_vm_id_reg(kvm, SYS_ID_AA64MMFR4_EL1, val);
>
> - /* Only limited support for PMU, Debug, BPs and WPs */
> + /* Only limited support for PMU, Debug, BPs, WPs, and HPMN0 */
> val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1);
> val &= (NV_FTR(DFR0, PMUVer) |
> NV_FTR(DFR0, WRPs) |
> NV_FTR(DFR0, BRPs) |
> - NV_FTR(DFR0, DebugVer));
> + NV_FTR(DFR0, DebugVer) |
> + NV_FTR(DFR0, HPMN0));
I don't think it does what you say it does! ;-)
What you probably want is something like
val |= FIELD_PREP(NV_FTR(DFR0, HPMN0),
ID_AA64DFR0_EL1_HPMN0_DEF);
instead.
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-08-27 16:40 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-27 0:22 [PATCH v2 00/12] KVM: arm64: nv: More complete support for FEAT_PMUv3 Oliver Upton
2024-08-27 0:22 ` [PATCH v2 01/12] KVM: arm64: Rename kvm_pmu_valid_counter_mask() Oliver Upton
2024-08-27 0:22 ` [PATCH v2 02/12] KVM: arm64: nv: Adjust range of accessible PMCs according to HPMN Oliver Upton
2024-08-27 0:22 ` [PATCH v2 03/12] KVM: arm64: nv: Rename BEHAVE_FORWARD_ANY Oliver Upton
2024-08-27 0:22 ` [PATCH v2 04/12] KVM: arm64: nv: Add support for describing traps that affect Host EL0 Oliver Upton
2024-08-27 16:24 ` Marc Zyngier
2024-08-27 0:22 ` [PATCH v2 05/12] KVM: arm64: nv: Reinject traps that take effect in " Oliver Upton
2024-08-27 0:22 ` [PATCH v2 06/12] KVM: arm64: nv: Honor MDCR_EL2.{TPM, TPMCR} " Oliver Upton
2024-08-27 0:22 ` [PATCH v2 07/12] KVM: arm64: nv: Allow coarse-grained trap combos to use complex traps Oliver Upton
2024-08-27 0:22 ` [PATCH v2 08/12] KVM: arm64: nv: Describe trap behaviour of MDCR_EL2.HPMN Oliver Upton
2024-08-27 0:22 ` [PATCH v2 09/12] KVM: arm64: nv: Advertise support for FEAT_HPMN0 Oliver Upton
2024-08-27 16:40 ` Marc Zyngier
2024-08-27 0:22 ` [PATCH v2 10/12] KVM: arm64: Add helpers to determine if PMC counts at a given EL Oliver Upton
2024-08-27 0:22 ` [PATCH v2 11/12] KVM: arm64: nv: Honor NSH filter when in hyp context Oliver Upton
2024-08-27 0:22 ` [PATCH v2 12/12] KVM: arm64: nv: Reprogram PMU events affected by nested transition Oliver Upton
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.