[PATCH v16 20/45] KVM: arm64: CCA: Support timers in realm RECs
From: Steven Price <steven.price@arm.com>
Date: 2026-08-03 13:45:52
Also in:
kvm, kvmarm, linux-arm-kernel, lkml
Subsystem:
arm64 port (aarch64 architecture), kernel virtual machine for arm64 (kvm/arm64), the rest · Maintainers:
Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton, Linus Torvalds
The RMM keeps track of the timer while the realm REC is running, but on exit to the normal world KVM is responsible for handling the timers. A later patch adds the support for propagating the timer values from the exit data structure and calling kvm_realm_timers_update(). Signed-off-by: Steven Price <steven.price@arm.com> --- Changes since v15: * Major rewrite to make better use of the KVM infrastructure. Changes since v14: * Special case in kvm_timer_vcpu_load()/kvm_timer_vcpu_put() the timer handling. Changes since v12: * Adapt to upstream changes. Changes since v11: * Drop the kvm_is_realm() check from timer_set_offset(). We already ensure that the offset is 0 when calling the function. Changes since v10: * KVM_CAP_COUNTER_OFFSET is now already hidden by a previous patch. Changes since v9: * No need to move the call to kvm_timer_unblocking() in kvm_timer_vcpu_load(). Changes since v7: * Hide KVM_CAP_COUNTER_OFFSET for realm guests. --- arch/arm64/kvm/arch_timer.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index 4155fe89b58a..00ff182ae288 100644
--- a/arch/arm64/kvm/arch_timer.c
+++ b/arch/arm64/kvm/arch_timer.c@@ -56,11 +56,22 @@ static unsigned long kvm_arch_timer_get_irq_flags(void) return kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0; } +static unsigned long kvm_realm_timer_get_irq_flags(void) +{ + /* RMI_REC_ENTER rejects LRs which have the HW bit set. */ + return VGIC_IRQ_SW_RESAMPLE; +} + static const struct irq_ops arch_timer_irq_ops = { .get_flags = kvm_arch_timer_get_irq_flags, .get_input_level = kvm_arch_timer_get_input_level, }; +static const struct irq_ops realm_timer_irq_ops = { + .get_flags = kvm_realm_timer_get_irq_flags, + .get_input_level = kvm_arch_timer_get_input_level, +}; + static const struct irq_ops arch_timer_irq_ops_vgic_v5 = { .get_input_level = kvm_arch_timer_get_input_level, .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,
@@ -1073,7 +1084,8 @@ static void timer_context_init(struct kvm_vcpu *vcpu, int timerid) ctxt->timer_id = timerid; - if (!kvm_vm_is_protected(vcpu->kvm)) { + /* The RMM architecture fixes both Realm counter offsets at zero. */ + if (!kvm_vm_is_protected(vcpu->kvm) && !vcpu_is_rec(vcpu)) { if (timerid == TIMER_VTIMER) ctxt->offset.vm_offset = &kvm->arch.timer_data.voffset; else
@@ -1103,8 +1115,11 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) for (int i = 0; i < NR_KVM_TIMERS; i++) timer_context_init(vcpu, i); - /* Synchronize offsets across timers of a VM if not already provided */ - if (!vcpu_is_protected(vcpu) && + /* + * Synchronize offsets across timers of a VM if not already provided. + * Realm counter offsets are fixed at zero by the RMM architecture. + */ + if (!vcpu_is_protected(vcpu) && !vcpu_is_rec(vcpu) && !test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) { timer_set_offset(vcpu_vtimer(vcpu), kvm_phys_timer_read()); timer_set_offset(vcpu_ptimer(vcpu), 0);
@@ -1602,8 +1617,15 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) get_timer_map(vcpu, &map); - ops = vgic_is_v5(vcpu->kvm) ? &arch_timer_irq_ops_vgic_v5 : - &arch_timer_irq_ops; + /* + * RMI_REC_ENTER rejects LRs with the HW bit set, so use the existing + * software resampling mechanism for Realm timer interrupts. + */ + if (vcpu_is_rec(vcpu)) + ops = &realm_timer_irq_ops; + else + ops = vgic_is_v5(vcpu->kvm) ? &arch_timer_irq_ops_vgic_v5 : + &arch_timer_irq_ops; for (int i = 0; i < nr_timers(vcpu); i++) kvm_vgic_set_irq_ops(vcpu, timer_irq(vcpu_get_timer(vcpu, i)), ops);
@@ -1729,7 +1751,7 @@ int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm, if (offset->reserved) return -EINVAL; - if (kvm_vm_is_protected(kvm)) + if (kvm_vm_is_protected(kvm) || kvm_is_realm(kvm)) return -EINVAL; mutex_lock(&kvm->lock);
--
2.43.0