aboutsummaryrefslogtreecommitdiffstats
path: root/include/kvm/arm_arch_timer.h
diff options
context:
space:
mode:
authorMarc Zyngier <[email protected]>2024-12-17 14:23:19 +0000
committerMarc Zyngier <[email protected]>2025-01-02 19:19:10 +0000
commit0bc9a9e85fcf4ffb69846b961273fde4eb0d03ab (patch)
tree4ac9a821ff5cad979e7844d54a955d5757fe81ab /include/kvm/arm_arch_timer.h
parentKVM: arm64: nv: Sanitise CNTHCTL_EL2 (diff)
downloadkernel-0bc9a9e85fcf4ffb69846b961273fde4eb0d03ab.tar.gz
kernel-0bc9a9e85fcf4ffb69846b961273fde4eb0d03ab.zip
KVM: arm64: Work around x1e's CNTVOFF_EL2 bogosity
It appears that on Qualcomm's x1e CPU, CNTVOFF_EL2 doesn't really work, specially with HCR_EL2.E2H=1. A non-zero offset results in a screaming virtual timer interrupt, to the tune of a few 100k interrupts per second on a 4 vcpu VM. This is also evidenced by this CPU's inability to correctly run any of the timer selftests. The only case this doesn't break is when this register is set to 0, which breaks VM migration. When HCR_EL2.E2H=0, the timer seems to behave normally, and does not result in an interrupt storm. As a workaround, use the fact that this CPU implements FEAT_ECV, and trap all accesses to the virtual timer and counter, keeping CNTVOFF_EL2 set to zero, and emulate accesses to CVAL/TVAL/CTL and the counter itself, fixing up the timer to account for the missing offset. And if you think this is disgusting, you'd probably be right. Acked-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
Diffstat (limited to 'include/kvm/arm_arch_timer.h')
-rw-r--r--include/kvm/arm_arch_timer.h7
1 files changed, 7 insertions, 0 deletions
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index c1ba31fab6f5..681cf0c8b9df 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -151,6 +151,13 @@ void kvm_timer_cpu_down(void);
/* CNTKCTL_EL1 valid bits as of DDI0487J.a */
#define CNTKCTL_VALID_BITS (BIT(17) | GENMASK_ULL(9, 0))
+DECLARE_STATIC_KEY_FALSE(broken_cntvoff_key);
+
+static inline bool has_broken_cntvoff(void)
+{
+ return static_branch_unlikely(&broken_cntvoff_key);
+}
+
static inline bool has_cntpoff(void)
{
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));