]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: nv: Handle L2->L1 transition on interrupt injection
authorMarc Zyngier <maz@kernel.org>
Tue, 25 Feb 2025 17:29:23 +0000 (17:29 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 3 Mar 2025 22:57:10 +0000 (14:57 -0800)
An interrupt being delivered to L1 while running L2 must result
in the correct exception being delivered to L1.

This means that if, on entry to L2, we found ourselves with pending
interrupts in the L1 distributor, we need to take immediate action.
This is done by posting a request which will prevent the entry in
L2, and deliver an IRQ exception to L1, forcing the switch.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250225172930.1850838-10-maz@kernel.org
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/arm.c
arch/arm64/kvm/nested.c
arch/arm64/kvm/vgic/vgic.c

index 7e0dc6b4cb99e70808c659a349facc475ab36659..86519a73971ed6f514b530d73e0610bee1caa3e2 100644 (file)
 
 #define KVM_REQ_SLEEP \
        KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
-#define KVM_REQ_IRQ_PENDING    KVM_ARCH_REQ(1)
-#define KVM_REQ_VCPU_RESET     KVM_ARCH_REQ(2)
-#define KVM_REQ_RECORD_STEAL   KVM_ARCH_REQ(3)
-#define KVM_REQ_RELOAD_GICv4   KVM_ARCH_REQ(4)
-#define KVM_REQ_RELOAD_PMU     KVM_ARCH_REQ(5)
-#define KVM_REQ_SUSPEND                KVM_ARCH_REQ(6)
-#define KVM_REQ_RESYNC_PMU_EL0 KVM_ARCH_REQ(7)
-#define KVM_REQ_NESTED_S2_UNMAP        KVM_ARCH_REQ(8)
+#define KVM_REQ_IRQ_PENDING            KVM_ARCH_REQ(1)
+#define KVM_REQ_VCPU_RESET             KVM_ARCH_REQ(2)
+#define KVM_REQ_RECORD_STEAL           KVM_ARCH_REQ(3)
+#define KVM_REQ_RELOAD_GICv4           KVM_ARCH_REQ(4)
+#define KVM_REQ_RELOAD_PMU             KVM_ARCH_REQ(5)
+#define KVM_REQ_SUSPEND                        KVM_ARCH_REQ(6)
+#define KVM_REQ_RESYNC_PMU_EL0         KVM_ARCH_REQ(7)
+#define KVM_REQ_NESTED_S2_UNMAP                KVM_ARCH_REQ(8)
+#define KVM_REQ_GUEST_HYP_IRQ_PENDING  KVM_ARCH_REQ(9)
 
 #define KVM_DIRTY_LOG_MANUAL_CAPS   (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
                                     KVM_DIRTY_LOG_INITIALLY_SET)
index 383aa48622344b571c6df135dabf911f6c22cd7c..0ec7099f4b63a7a945885dd00ce88b3320f0f923 100644 (file)
@@ -1148,6 +1148,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
                 * preserved on VMID roll-over if the task was preempted,
                 * making a thread's VMID inactive. So we need to call
                 * kvm_arm_vmid_update() in non-premptible context.
+                *
+                * Note that this must happen after the check_vcpu_request()
+                * call to pick the correct s2_mmu structure, as a pending
+                * nested exception (IRQ, for example) can trigger a change
+                * in translation regime.
                 */
                if (kvm_arm_vmid_update(&vcpu->arch.hw_mmu->vmid) &&
                    has_vhe())
index 7c8f39070a50386ae29175acaa4231ba01b19cd4..722e61e410e2e1c0f3ab85c6382daed75a734ad4 100644 (file)
@@ -1318,4 +1318,8 @@ void check_nested_vcpu_requests(struct kvm_vcpu *vcpu)
                }
                write_unlock(&vcpu->kvm->mmu_lock);
        }
+
+       /* Must be last, as may switch context! */
+       if (kvm_check_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu))
+               kvm_inject_nested_irq(vcpu);
 }
index 324c547e1b4d813e485f9c1f5c9749a4124b9c02..9734a71b8561130d60f259bbc8334e00df2b3b40 100644 (file)
@@ -906,6 +906,29 @@ static inline void vgic_restore_state(struct kvm_vcpu *vcpu)
 /* Flush our emulation state into the GIC hardware before entering the guest. */
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
 {
+       /*
+        * If in a nested state, we must return early. Two possibilities:
+        *
+        * - If we have any pending IRQ for the guest and the guest
+        *   expects IRQs to be handled in its virtual EL2 mode (the
+        *   virtual IMO bit is set) and it is not already running in
+        *   virtual EL2 mode, then we have to emulate an IRQ
+        *   exception to virtual EL2.
+        *
+        *   We do that by placing a request to ourselves which will
+        *   abort the entry procedure and inject the exception at the
+        *   beginning of the run loop.
+        *
+        * - Otherwise, do exactly *NOTHING*. The guest state is
+        *   already loaded, and we can carry on with running it.
+        */
+       if (vgic_state_is_nested(vcpu)) {
+               if (kvm_vgic_vcpu_pending_irq(vcpu))
+                       kvm_make_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu);
+
+               return;
+       }
+
        /*
         * If there are no virtual interrupts active or pending for this
         * VCPU, then there is no work to do and we can bail out without