* [PATCH v3 1/6] KVM: arm64: PMU: Set raw values from user to PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR}
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
@ 2025-03-12 11:55 ` Akihiko Odaki
2025-03-12 11:55 ` [PATCH v3 2/6] KVM: arm64: PMU: Assume PMU presence in pmu-emul.c Akihiko Odaki
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:55 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki,
stable
Commit a45f41d754e0 ("KVM: arm64: Add {get,set}_user for
PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR}") changed KVM_SET_ONE_REG to update
the mentioned registers in a way matching with the behavior of guest
register writes. This is a breaking change of a UAPI though the new
semantics looks cleaner and VMMs are not prepared for this.
Firecracker, QEMU, and crosvm perform migration by listing registers
with KVM_GET_REG_LIST, getting their values with KVM_GET_ONE_REG and
setting them with KVM_SET_ONE_REG. This algorithm assumes
KVM_SET_ONE_REG restores the values retrieved with KVM_GET_ONE_REG
without any alteration. However, bit operations added by the earlier
commit do not preserve the values retried with KVM_GET_ONE_REG and
potentially break migration.
Remove the bit operations that alter the values retrieved with
KVM_GET_ONE_REG.
Cc: stable@vger.kernel.org
Fixes: a45f41d754e0 ("KVM: arm64: Add {get,set}_user for PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR}")
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Acked-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 21 +--------------------
1 file changed, 1 insertion(+), 20 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 42791971f758..0a2ce931a946 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1126,26 +1126,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 val)
{
- bool set;
-
- val &= kvm_pmu_valid_counter_mask(vcpu);
-
- switch (r->reg) {
- case PMOVSSET_EL0:
- /* CRm[1] being set indicates a SET register, and CLR otherwise */
- set = r->CRm & 2;
- break;
- default:
- /* Op2[0] being set indicates a SET register, and CLR otherwise */
- set = r->Op2 & 1;
- break;
- }
-
- if (set)
- __vcpu_sys_reg(vcpu, r->reg) |= val;
- else
- __vcpu_sys_reg(vcpu, r->reg) &= ~val;
-
+ __vcpu_sys_reg(vcpu, r->reg) = val & kvm_pmu_valid_counter_mask(vcpu);
return 0;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 2/6] KVM: arm64: PMU: Assume PMU presence in pmu-emul.c
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
2025-03-12 11:55 ` [PATCH v3 1/6] KVM: arm64: PMU: Set raw values from user to PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR} Akihiko Odaki
@ 2025-03-12 11:55 ` Akihiko Odaki
2025-03-12 21:08 ` Oliver Upton
2025-03-12 11:55 ` [PATCH v3 3/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
` (3 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:55 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki
Many functions in pmu-emul.c checks kvm_vcpu_has_pmu(vcpu). A favorable
interpretation is defensive programming, but it also has downsides:
- It is confusing as it implies these functions are called without PMU
although most of them are called only when a PMU is present.
- It makes semantics of functions fuzzy. For example, calling
kvm_pmu_disable_counter_mask() without PMU may result in no-op as
there are no enabled counters, but it's unclear what
kvm_pmu_get_counter_value() returns when there is no PMU.
- It allows callers without checking kvm_vcpu_has_pmu(vcpu), but it is
often wrong to call these functions without PMU.
- It is error-prone to duplicate kvm_vcpu_has_pmu(vcpu) checks into
multiple functions. Many functions are called for system registers,
and the system register infrastructure already employs less
error-prone, comprehensive checks.
Check kvm_vcpu_has_pmu(vcpu) in callers of these functions instead,
and remove the obsolete checks from pmu-emul.c.
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
arch/arm64/kvm/arm.c | 8 +++++---
arch/arm64/kvm/guest.c | 12 ++++++++++++
arch/arm64/kvm/pmu-emul.c | 34 ++--------------------------------
arch/arm64/kvm/sys_regs.c | 6 ++++--
4 files changed, 23 insertions(+), 37 deletions(-)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index f66ce098f03b..e375468a2217 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -834,9 +834,11 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
if (ret)
return ret;
- ret = kvm_arm_pmu_v3_enable(vcpu);
- if (ret)
- return ret;
+ if (kvm_vcpu_has_pmu(vcpu)) {
+ ret = kvm_arm_pmu_v3_enable(vcpu);
+ if (ret)
+ return ret;
+ }
if (is_protected_kvm_enabled()) {
ret = pkvm_create_hyp_vm(kvm);
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 962f985977c2..fc09eec3fd94 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -951,6 +951,10 @@ int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
switch (attr->group) {
case KVM_ARM_VCPU_PMU_V3_CTRL:
+ if (!kvm_vcpu_has_pmu(vcpu)) {
+ ret = -ENODEV;
+ break;
+ }
mutex_lock(&vcpu->kvm->arch.config_lock);
ret = kvm_arm_pmu_v3_set_attr(vcpu, attr);
mutex_unlock(&vcpu->kvm->arch.config_lock);
@@ -976,6 +980,10 @@ int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
switch (attr->group) {
case KVM_ARM_VCPU_PMU_V3_CTRL:
+ if (!kvm_vcpu_has_pmu(vcpu)) {
+ ret = -ENODEV;
+ break;
+ }
ret = kvm_arm_pmu_v3_get_attr(vcpu, attr);
break;
case KVM_ARM_VCPU_TIMER_CTRL:
@@ -999,6 +1007,10 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
switch (attr->group) {
case KVM_ARM_VCPU_PMU_V3_CTRL:
+ if (!kvm_vcpu_has_pmu(vcpu)) {
+ ret = -ENXIO;
+ break;
+ }
ret = kvm_arm_pmu_v3_has_attr(vcpu, attr);
break;
case KVM_ARM_VCPU_TIMER_CTRL:
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index e3e82b66e226..3e5bf414447f 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -144,9 +144,6 @@ static u64 kvm_pmu_get_pmc_value(struct kvm_pmc *pmc)
*/
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx)
{
- if (!kvm_vcpu_has_pmu(vcpu))
- return 0;
-
return kvm_pmu_get_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, select_idx));
}
@@ -185,9 +182,6 @@ static void kvm_pmu_set_pmc_value(struct kvm_pmc *pmc, u64 val, bool force)
*/
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
{
- if (!kvm_vcpu_has_pmu(vcpu))
- return;
-
kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, select_idx), val, false);
}
@@ -289,8 +283,6 @@ u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
{
int i;
- if (!kvm_vcpu_has_pmu(vcpu))
- return;
if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) || !val)
return;
@@ -324,7 +316,7 @@ void kvm_pmu_disable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
{
int i;
- if (!kvm_vcpu_has_pmu(vcpu) || !val)
+ if (!val)
return;
for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++) {
@@ -357,9 +349,6 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu)
struct kvm_pmu *pmu = &vcpu->arch.pmu;
bool overflow;
- if (!kvm_vcpu_has_pmu(vcpu))
- return;
-
overflow = !!kvm_pmu_overflow_status(vcpu);
if (pmu->irq_level == overflow)
return;
@@ -555,9 +544,6 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
{
int i;
- if (!kvm_vcpu_has_pmu(vcpu))
- return;
-
/* Fixup PMCR_EL0 to reconcile the PMU version and the LP bit */
if (!kvm_has_feat(vcpu->kvm, ID_AA64DFR0_EL1, PMUVer, V3P5))
val &= ~ARMV8_PMU_PMCR_LP;
@@ -696,9 +682,6 @@ void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, select_idx);
u64 reg;
- if (!kvm_vcpu_has_pmu(vcpu))
- return;
-
reg = counter_index_to_evtreg(pmc->idx);
__vcpu_sys_reg(vcpu, reg) = data & kvm_pmu_evtyper_mask(vcpu->kvm);
@@ -804,9 +787,6 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
u64 val, mask = 0;
int base, i, nr_events;
- if (!kvm_vcpu_has_pmu(vcpu))
- return 0;
-
if (!pmceid1) {
val = compute_pmceid0(cpu_pmu);
base = 0;
@@ -847,9 +827,6 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
{
- if (!kvm_vcpu_has_pmu(vcpu))
- return 0;
-
if (!vcpu->arch.pmu.created)
return -EINVAL;
@@ -1022,9 +999,6 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
lockdep_assert_held(&kvm->arch.config_lock);
- if (!kvm_vcpu_has_pmu(vcpu))
- return -ENODEV;
-
if (vcpu->arch.pmu.created)
return -EBUSY;
@@ -1129,9 +1103,6 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
if (!irqchip_in_kernel(vcpu->kvm))
return -EINVAL;
- if (!kvm_vcpu_has_pmu(vcpu))
- return -ENODEV;
-
if (!kvm_arm_pmu_irq_initialized(vcpu))
return -ENXIO;
@@ -1150,8 +1121,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
case KVM_ARM_VCPU_PMU_V3_INIT:
case KVM_ARM_VCPU_PMU_V3_FILTER:
case KVM_ARM_VCPU_PMU_V3_SET_PMU:
- if (kvm_vcpu_has_pmu(vcpu))
- return 0;
+ return 0;
}
return -ENXIO;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 0a2ce931a946..6e75557bea1d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1784,12 +1784,14 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
static u64 read_sanitised_id_dfr0_el1(struct kvm_vcpu *vcpu,
const struct sys_reg_desc *rd)
{
- u8 perfmon = pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
+ u8 perfmon;
u64 val = read_sanitised_ftr_reg(SYS_ID_DFR0_EL1);
val &= ~ID_DFR0_EL1_PerfMon_MASK;
- if (kvm_vcpu_has_pmu(vcpu))
+ if (kvm_vcpu_has_pmu(vcpu)) {
+ perfmon = pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
val |= SYS_FIELD_PREP(ID_DFR0_EL1, PerfMon, perfmon);
+ }
val = ID_REG_LIMIT_FIELD_ENUM(val, ID_DFR0_EL1, CopDbg, Debugv8p8);
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 2/6] KVM: arm64: PMU: Assume PMU presence in pmu-emul.c
2025-03-12 11:55 ` [PATCH v3 2/6] KVM: arm64: PMU: Assume PMU presence in pmu-emul.c Akihiko Odaki
@ 2025-03-12 21:08 ` Oliver Upton
0 siblings, 0 replies; 10+ messages in thread
From: Oliver Upton @ 2025-03-12 21:08 UTC (permalink / raw)
To: Akihiko Odaki
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Andrew Jones, Shannon Zhao,
linux-arm-kernel, kvmarm, linux-kernel, devel
Hi Akihiko,
On Wed, Mar 12, 2025 at 08:55:56PM +0900, Akihiko Odaki wrote:
[...]
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 962f985977c2..fc09eec3fd94 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -951,6 +951,10 @@ int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
>
> switch (attr->group) {
> case KVM_ARM_VCPU_PMU_V3_CTRL:
> + if (!kvm_vcpu_has_pmu(vcpu)) {
> + ret = -ENODEV;
> + break;
> + }
> mutex_lock(&vcpu->kvm->arch.config_lock);
> ret = kvm_arm_pmu_v3_set_attr(vcpu, attr);
> mutex_unlock(&vcpu->kvm->arch.config_lock);
> @@ -976,6 +980,10 @@ int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
>
> switch (attr->group) {
> case KVM_ARM_VCPU_PMU_V3_CTRL:
> + if (!kvm_vcpu_has_pmu(vcpu)) {
> + ret = -ENODEV;
> + break;
> + }
> ret = kvm_arm_pmu_v3_get_attr(vcpu, attr);
> break;
> case KVM_ARM_VCPU_TIMER_CTRL:
> @@ -999,6 +1007,10 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>
> switch (attr->group) {
> case KVM_ARM_VCPU_PMU_V3_CTRL:
> + if (!kvm_vcpu_has_pmu(vcpu)) {
> + ret = -ENXIO;
> + break;
> + }
> ret = kvm_arm_pmu_v3_has_attr(vcpu, attr);
> break;
> case KVM_ARM_VCPU_TIMER_CTRL:
I agree with you for the most part on this patch, but I prefer we keep
the kvm_vcpu_has_pmu() with the ioctl implemementations rather than the
spot at which we demux the ioctl.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 3/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
2025-03-12 11:55 ` [PATCH v3 1/6] KVM: arm64: PMU: Set raw values from user to PM{C,I}NTEN{SET,CLR}, PMOVS{SET,CLR} Akihiko Odaki
2025-03-12 11:55 ` [PATCH v3 2/6] KVM: arm64: PMU: Assume PMU presence in pmu-emul.c Akihiko Odaki
@ 2025-03-12 11:55 ` Akihiko Odaki
2025-03-12 11:55 ` [PATCH v3 4/6] KVM: arm64: PMU: Reload when user modifies registers Akihiko Odaki
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:55 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki
Reload the perf event when setting the vPMU counter (vPMC) registers
(PMCCNTR_EL0 and PMEVCNTR<n>_EL0). This is a change corresponding to
commit 9228b26194d1 ("KVM: arm64: PMU: Fix GET_ONE_REG
for vPMC regs to return the current value") but for SET_ONE_REG.
Values of vPMC registers are saved in sysreg files on certain occasions.
These saved values don't represent the current values of the vPMC
registers if the perf events for the vPMCs count events after the save.
The current values of those registers are the sum of the sysreg file
value and the current perf event counter value. But, when userspace
writes those registers (using KVM_SET_ONE_REG), KVM only updates the
sysreg file value and leaves the current perf event counter value as is.
It is also important to keep the correct state even if userspace writes
them after first run, specifically when debugging Windows on QEMU with
GDB; QEMU tries to write back all visible registers when resuming the VM
execution with GDB, corrupting the PMU state. Windows always uses the
PMU so this can cause adverse effects on that particular OS.
Fix this by releasing the current perf event and trigger recreating one
with KVM_REQ_RELOAD_PMU.
Fixes: 051ff581ce70 ("arm64: KVM: Add access handler for event counter register")
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
arch/arm64/kvm/pmu-emul.c | 13 +++++++++++++
arch/arm64/kvm/sys_regs.c | 20 +++++++++++++++++++-
include/kvm/arm_pmu.h | 1 +
3 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 3e5bf414447f..1cfe53b6353e 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -185,6 +185,19 @@ void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, select_idx), val, false);
}
+/**
+ * kvm_pmu_set_counter_value_user - set PMU counter value from user
+ * @vcpu: The vcpu pointer
+ * @select_idx: The counter index
+ * @val: The counter value
+ */
+void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
+{
+ kvm_pmu_release_perf_event(kvm_vcpu_idx_to_pmc(vcpu, select_idx));
+ __vcpu_sys_reg(vcpu, counter_index_to_reg(select_idx)) = val;
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+}
+
/**
* kvm_pmu_release_perf_event - remove the perf event
* @pmc: The PMU counter pointer
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 6e75557bea1d..26182cae4ac7 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1035,6 +1035,22 @@ static int get_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
return 0;
}
+static int set_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
+ u64 val)
+{
+ u64 idx;
+
+ if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 0)
+ /* PMCCNTR_EL0 */
+ idx = ARMV8_PMU_CYCLE_IDX;
+ else
+ /* PMEVCNTRn_EL0 */
+ idx = ((r->CRm & 3) << 3) | (r->Op2 & 7);
+
+ kvm_pmu_set_counter_value_user(vcpu, idx, val);
+ return 0;
+}
+
static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
@@ -1309,6 +1325,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
#define PMU_PMEVCNTR_EL0(n) \
{ PMU_SYS_REG(PMEVCNTRn_EL0(n)), \
.reset = reset_pmevcntr, .get_user = get_pmu_evcntr, \
+ .set_user = set_pmu_evcntr, \
.access = access_pmu_evcntr, .reg = (PMEVCNTR0_EL0 + n), }
/* Macro to expand the PMEVTYPERn_EL0 register */
@@ -2665,7 +2682,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
.access = access_pmceid, .reset = NULL },
{ PMU_SYS_REG(PMCCNTR_EL0),
.access = access_pmu_evcntr, .reset = reset_unknown,
- .reg = PMCCNTR_EL0, .get_user = get_pmu_evcntr},
+ .reg = PMCCNTR_EL0, .get_user = get_pmu_evcntr,
+ .set_user = set_pmu_evcntr },
{ PMU_SYS_REG(PMXEVTYPER_EL0),
.access = access_pmu_evtyper, .reset = NULL },
{ PMU_SYS_REG(PMXEVCNTR_EL0),
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 28b380ad8dfa..9c062756ebfa 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -41,6 +41,7 @@ bool kvm_supports_guest_pmuv3(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);
+void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
u64 kvm_pmu_valid_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);
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 4/6] KVM: arm64: PMU: Reload when user modifies registers
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
` (2 preceding siblings ...)
2025-03-12 11:55 ` [PATCH v3 3/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
@ 2025-03-12 11:55 ` Akihiko Odaki
2025-03-12 21:18 ` Oliver Upton
2025-03-12 11:55 ` [PATCH v3 5/6] KVM: arm64: PMU: Call kvm_pmu_handle_pmcr() after masking PMCNTENSET_EL0 Akihiko Odaki
2025-03-12 11:56 ` [PATCH v3 6/6] KVM: arm64: Reload PMCNTENSET_EL0 Akihiko Odaki
5 siblings, 1 reply; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:55 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki
Commit d0c94c49792c ("KVM: arm64: Restore PMU configuration on first
run") added the code to reload the PMU configuration on first run.
It is also important to keep the correct state even if system registers
are modified after first run, specifically when debugging Windows on
QEMU with GDB; QEMU tries to write back all visible registers when
resuming the VM execution with GDB, corrupting the PMU state. Windows
always uses the PMU so this can cause adverse effects on that particular
OS.
The usual register writes are already handled independently, but
register writes from userspace and ones for reset are not covered.
Trigger the code to reload the PMU configuration in these code paths
instead so that PMU configuration changes made by users will be applied
also after the first run.
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
arch/arm64/kvm/pmu-emul.c | 3 ---
arch/arm64/kvm/sys_regs.c | 6 ++++++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 1cfe53b6353e..78cfa8b0964d 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -862,9 +862,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
return -EINVAL;
}
- /* One-off reload of the PMU on first run */
- kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
-
return 0;
}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 26182cae4ac7..307ce37d0434 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1143,6 +1143,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
static int set_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 val)
{
__vcpu_sys_reg(vcpu, r->reg) = val & kvm_pmu_valid_counter_mask(vcpu);
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
return 0;
}
@@ -1303,6 +1304,8 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
val |= ARMV8_PMU_PMCR_LC;
__vcpu_sys_reg(vcpu, r->reg) = val;
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
+
return 0;
}
@@ -4259,6 +4262,9 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
}
set_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags);
+
+ if (kvm_vcpu_has_pmu(vcpu))
+ kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
}
/**
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 4/6] KVM: arm64: PMU: Reload when user modifies registers
2025-03-12 11:55 ` [PATCH v3 4/6] KVM: arm64: PMU: Reload when user modifies registers Akihiko Odaki
@ 2025-03-12 21:18 ` Oliver Upton
0 siblings, 0 replies; 10+ messages in thread
From: Oliver Upton @ 2025-03-12 21:18 UTC (permalink / raw)
To: Akihiko Odaki
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Andrew Jones, Shannon Zhao,
linux-arm-kernel, kvmarm, linux-kernel, devel
On Wed, Mar 12, 2025 at 08:55:58PM +0900, Akihiko Odaki wrote:
> Commit d0c94c49792c ("KVM: arm64: Restore PMU configuration on first
> run") added the code to reload the PMU configuration on first run.
>
> It is also important to keep the correct state even if system registers
> are modified after first run, specifically when debugging Windows on
> QEMU with GDB; QEMU tries to write back all visible registers when
> resuming the VM execution with GDB, corrupting the PMU state. Windows
> always uses the PMU so this can cause adverse effects on that particular
> OS.
>
> The usual register writes are already handled independently, but
> register writes from userspace and ones for reset are not covered.
Ah -- that explains why you're moving the KVM_REQ_RELOAD_PMU from
kvm_arm_pmuv3_enable().
> @@ -4259,6 +4262,9 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
> }
>
> set_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags);
> +
> + if (kvm_vcpu_has_pmu(vcpu))
> + kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
nitpick, but maybe this can be added to kvm_pmu_vcpu_reset() instead.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 5/6] KVM: arm64: PMU: Call kvm_pmu_handle_pmcr() after masking PMCNTENSET_EL0
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
` (3 preceding siblings ...)
2025-03-12 11:55 ` [PATCH v3 4/6] KVM: arm64: PMU: Reload when user modifies registers Akihiko Odaki
@ 2025-03-12 11:55 ` Akihiko Odaki
2025-03-12 11:56 ` [PATCH v3 6/6] KVM: arm64: Reload PMCNTENSET_EL0 Akihiko Odaki
5 siblings, 0 replies; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:55 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki
kvm_pmu_handle_pmcr() expects PMCNTENSET_EL0 only contains valid
counters.
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
arch/arm64/kvm/pmu-emul.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 78cfa8b0964d..2d19c6048091 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -831,11 +831,11 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
{
u64 mask = kvm_pmu_valid_counter_mask(vcpu);
- kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu));
-
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) &= mask;
__vcpu_sys_reg(vcpu, PMINTENSET_EL1) &= mask;
__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) &= mask;
+
+ kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu));
}
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 6/6] KVM: arm64: Reload PMCNTENSET_EL0
2025-03-12 11:55 [PATCH v3 0/6] KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs Akihiko Odaki
` (4 preceding siblings ...)
2025-03-12 11:55 ` [PATCH v3 5/6] KVM: arm64: PMU: Call kvm_pmu_handle_pmcr() after masking PMCNTENSET_EL0 Akihiko Odaki
@ 2025-03-12 11:56 ` Akihiko Odaki
2025-03-12 21:23 ` Oliver Upton
5 siblings, 1 reply; 10+ messages in thread
From: Akihiko Odaki @ 2025-03-12 11:56 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Andrew Jones,
Shannon Zhao
Cc: linux-arm-kernel, kvmarm, linux-kernel, devel, Akihiko Odaki
Disable counters that are no longer included in PMCNTENSET_EL0. It is
not necessary to enable counters included in PMCNTENSET_EL0 because
kvm_pmu_handle_pmcr() does so if appropriate.
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
arch/arm64/kvm/pmu-emul.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 2d19c6048091..b14655dda6db 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -831,6 +831,8 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
{
u64 mask = kvm_pmu_valid_counter_mask(vcpu);
+ kvm_pmu_disable_counter_mask(vcpu, ~__vcpu_sys_reg(vcpu, PMCNTENSET_EL0));
+
__vcpu_sys_reg(vcpu, PMOVSSET_EL0) &= mask;
__vcpu_sys_reg(vcpu, PMINTENSET_EL1) &= mask;
__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) &= mask;
--
2.48.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 6/6] KVM: arm64: Reload PMCNTENSET_EL0
2025-03-12 11:56 ` [PATCH v3 6/6] KVM: arm64: Reload PMCNTENSET_EL0 Akihiko Odaki
@ 2025-03-12 21:23 ` Oliver Upton
0 siblings, 0 replies; 10+ messages in thread
From: Oliver Upton @ 2025-03-12 21:23 UTC (permalink / raw)
To: Akihiko Odaki
Cc: Marc Zyngier, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Andrew Jones, Shannon Zhao,
linux-arm-kernel, kvmarm, linux-kernel, devel
On Wed, Mar 12, 2025 at 08:56:00PM +0900, Akihiko Odaki wrote:
> Disable counters that are no longer included in PMCNTENSET_EL0. It is
> not necessary to enable counters included in PMCNTENSET_EL0 because
> kvm_pmu_handle_pmcr() does so if appropriate.
>
> Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
> ---
> arch/arm64/kvm/pmu-emul.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
> index 2d19c6048091..b14655dda6db 100644
> --- a/arch/arm64/kvm/pmu-emul.c
> +++ b/arch/arm64/kvm/pmu-emul.c
> @@ -831,6 +831,8 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
> {
> u64 mask = kvm_pmu_valid_counter_mask(vcpu);
>
> + kvm_pmu_disable_counter_mask(vcpu, ~__vcpu_sys_reg(vcpu, PMCNTENSET_EL0));
> +
Just so this function appears consistent, can we move this after the
point where the mask is applied?
There's no functional impact of course since PMCR_EL0.N can only be
changed before the VM is started, i.e. not possible to have running
counters >= N.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 10+ messages in thread