]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Add timer UAPI workaround to sysreg infrastructure
authorMarc Zyngier <maz@kernel.org>
Mon, 29 Sep 2025 16:04:49 +0000 (17:04 +0100)
committerMarc Zyngier <maz@kernel.org>
Mon, 13 Oct 2025 13:42:40 +0000 (14:42 +0100)
Amongst the numerous bugs that plague the KVM/arm64 UAPI, one of
the most annoying thing is that the userspace view of the virtual
timer has its CVAL and CNT encodings swapped.

In order to reduce the amount of code that has to know about this,
start by adding handling for this bug in the sys_reg code.

Nothing is making use of it yet, as the code responsible for userspace
interaction is catching the accesses early.

Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/sys_regs.h

index 9f2f4e0b042e8715230fe26c106e71541d6c713c..8e6f50f54b4bfad828013940ea67dca6ca58272e 100644 (file)
@@ -5231,15 +5231,28 @@ static int demux_c15_set(struct kvm_vcpu *vcpu, u64 id, void __user *uaddr)
        }
 }
 
+static u64 kvm_one_reg_to_id(const struct kvm_one_reg *reg)
+{
+       switch(reg->id) {
+       case KVM_REG_ARM_TIMER_CVAL:
+               return TO_ARM64_SYS_REG(CNTV_CVAL_EL0);
+       case KVM_REG_ARM_TIMER_CNT:
+               return TO_ARM64_SYS_REG(CNTVCT_EL0);
+       default:
+               return reg->id;
+       }
+}
+
 int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
                         const struct sys_reg_desc table[], unsigned int num)
 {
        u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
        const struct sys_reg_desc *r;
+       u64 id = kvm_one_reg_to_id(reg);
        u64 val;
        int ret;
 
-       r = id_to_sys_reg_desc(vcpu, reg->id, table, num);
+       r = id_to_sys_reg_desc(vcpu, id, table, num);
        if (!r || sysreg_hidden(vcpu, r))
                return -ENOENT;
 
@@ -5272,13 +5285,14 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
 {
        u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr;
        const struct sys_reg_desc *r;
+       u64 id = kvm_one_reg_to_id(reg);
        u64 val;
        int ret;
 
        if (get_user(val, uaddr))
                return -EFAULT;
 
-       r = id_to_sys_reg_desc(vcpu, reg->id, table, num);
+       r = id_to_sys_reg_desc(vcpu, id, table, num);
        if (!r || sysreg_hidden(vcpu, r))
                return -ENOENT;
 
@@ -5338,10 +5352,23 @@ static u64 sys_reg_to_index(const struct sys_reg_desc *reg)
 
 static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
 {
+       u64 idx;
+
        if (!*uind)
                return true;
 
-       if (put_user(sys_reg_to_index(reg), *uind))
+       switch (reg_to_encoding(reg)) {
+       case SYS_CNTV_CVAL_EL0:
+               idx = KVM_REG_ARM_TIMER_CVAL;
+               break;
+       case SYS_CNTVCT_EL0:
+               idx = KVM_REG_ARM_TIMER_CNT;
+               break;
+       default:
+               idx = sys_reg_to_index(reg);
+       }
+
+       if (put_user(idx, *uind))
                return false;
 
        (*uind)++;
index 317abc490368d08be690602bf56774f504bdc39e..b3f904472fac563e904579c9befd3469b368849c 100644 (file)
@@ -257,4 +257,10 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu);
        (val);                                                                 \
 })
 
+#define TO_ARM64_SYS_REG(r)    ARM64_SYS_REG(sys_reg_Op0(SYS_ ## r),   \
+                                             sys_reg_Op1(SYS_ ## r),   \
+                                             sys_reg_CRn(SYS_ ## r),   \
+                                             sys_reg_CRm(SYS_ ## r),   \
+                                             sys_reg_Op2(SYS_ ## r))
+
 #endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */