]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mshv: add arm64 support for doorbell & intercept SINTs
authorAnirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Wed, 25 Feb 2026 12:44:03 +0000 (12:44 +0000)
committerWei Liu <wei.liu@kernel.org>
Wed, 25 Feb 2026 19:09:49 +0000 (19:09 +0000)
On x86, the HYPERVISOR_CALLBACK_VECTOR is used to receive synthetic
interrupts (SINTs) from the hypervisor for doorbells and intercepts.
There is no such vector reserved for arm64.

On arm64, the hypervisor exposes a synthetic register that can be read
to find the INTID that should be used for SINTs. This INTID is in the
PPI range.

To better unify the code paths, introduce mshv_sint_vector_init() that
either reads the synthetic register and obtains the INTID (arm64) or
just uses HYPERVISOR_CALLBACK_VECTOR as the interrupt vector (x86).

Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
drivers/hv/mshv_synic.c
include/hyperv/hvgdk_mini.h

index 617e8c02e365e376f9f109f1d91bc65920fee44b..43f1bcbbf2d34dede6656ebab38468159b567ee5 100644 (file)
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/random.h>
 #include <linux/cpuhotplug.h>
 #include <linux/reboot.h>
 #include <asm/mshyperv.h>
+#include <linux/acpi.h>
 
 #include "mshv_eventfd.h"
 #include "mshv.h"
 
 static int synic_cpuhp_online;
 static struct hv_synic_pages __percpu *synic_pages;
+static int mshv_sint_vector = -1; /* hwirq for the SynIC SINTs */
+static int mshv_sint_irq = -1; /* Linux IRQ for mshv_sint_vector */
 
 static u32 synic_event_ring_get_queued_port(u32 sint_index)
 {
@@ -442,9 +446,7 @@ void mshv_isr(void)
                if (msg->header.message_flags.msg_pending)
                        hv_set_non_nested_msr(HV_MSR_EOM, 0);
 
-#ifdef HYPERVISOR_CALLBACK_VECTOR
-               add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR);
-#endif
+               add_interrupt_randomness(mshv_sint_vector);
        } else {
                pr_warn_once("%s: unknown message type 0x%x\n", __func__,
                             msg->header.message_type);
@@ -456,9 +458,7 @@ static int mshv_synic_cpu_init(unsigned int cpu)
        union hv_synic_simp simp;
        union hv_synic_siefp siefp;
        union hv_synic_sirbp sirbp;
-#ifdef HYPERVISOR_CALLBACK_VECTOR
        union hv_synic_sint sint;
-#endif
        union hv_synic_scontrol sctrl;
        struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
        struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
@@ -501,10 +501,12 @@ static int mshv_synic_cpu_init(unsigned int cpu)
 
        hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
 
-#ifdef HYPERVISOR_CALLBACK_VECTOR
+       if (mshv_sint_irq != -1)
+               enable_percpu_irq(mshv_sint_irq, 0);
+
        /* Enable intercepts */
        sint.as_uint64 = 0;
-       sint.vector = HYPERVISOR_CALLBACK_VECTOR;
+       sint.vector = mshv_sint_vector;
        sint.masked = false;
        sint.auto_eoi = hv_recommend_using_aeoi();
        hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
@@ -512,13 +514,12 @@ static int mshv_synic_cpu_init(unsigned int cpu)
 
        /* Doorbell SINT */
        sint.as_uint64 = 0;
-       sint.vector = HYPERVISOR_CALLBACK_VECTOR;
+       sint.vector = mshv_sint_vector;
        sint.masked = false;
        sint.as_intercept = 1;
        sint.auto_eoi = hv_recommend_using_aeoi();
        hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
                              sint.as_uint64);
-#endif
 
        /* Enable global synic bit */
        sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
@@ -573,6 +574,9 @@ static int mshv_synic_cpu_exit(unsigned int cpu)
        hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
                              sint.as_uint64);
 
+       if (mshv_sint_irq != -1)
+               disable_percpu_irq(mshv_sint_irq);
+
        /* Disable Synic's event ring page */
        sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
        sirbp.sirbp_enabled = false;
@@ -683,14 +687,106 @@ static struct notifier_block mshv_synic_reboot_nb = {
        .notifier_call = mshv_synic_reboot_notify,
 };
 
+#ifndef HYPERVISOR_CALLBACK_VECTOR
+static DEFINE_PER_CPU(long, mshv_evt);
+
+static irqreturn_t mshv_percpu_isr(int irq, void *dev_id)
+{
+       mshv_isr();
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_ACPI
+static int __init mshv_acpi_setup_sint_irq(void)
+{
+       return acpi_register_gsi(NULL, mshv_sint_vector, ACPI_EDGE_SENSITIVE,
+                                       ACPI_ACTIVE_HIGH);
+}
+
+static void mshv_acpi_cleanup_sint_irq(void)
+{
+       acpi_unregister_gsi(mshv_sint_vector);
+}
+#else
+static int __init mshv_acpi_setup_sint_irq(void)
+{
+       return -ENODEV;
+}
+
+static void mshv_acpi_cleanup_sint_irq(void)
+{
+}
+#endif
+
+static int __init mshv_sint_vector_setup(void)
+{
+       int ret;
+       struct hv_register_assoc reg = {
+               .name = HV_ARM64_REGISTER_SINT_RESERVED_INTERRUPT_ID,
+       };
+       union hv_input_vtl input_vtl = { 0 };
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF,
+                               1, input_vtl, &reg);
+       if (ret || !reg.value.reg64)
+               return -ENODEV;
+
+       mshv_sint_vector = reg.value.reg64;
+       ret = mshv_acpi_setup_sint_irq();
+       if (ret < 0) {
+               pr_err("Failed to setup IRQ for MSHV SINT vector %d: %d\n",
+                       mshv_sint_vector, ret);
+               goto out_fail;
+       }
+
+       mshv_sint_irq = ret;
+
+       ret = request_percpu_irq(mshv_sint_irq, mshv_percpu_isr, "MSHV",
+               &mshv_evt);
+       if (ret)
+               goto out_unregister;
+
+       return 0;
+
+out_unregister:
+       mshv_acpi_cleanup_sint_irq();
+out_fail:
+       return ret;
+}
+
+static void mshv_sint_vector_cleanup(void)
+{
+       free_percpu_irq(mshv_sint_irq, &mshv_evt);
+       mshv_acpi_cleanup_sint_irq();
+}
+#else /* !HYPERVISOR_CALLBACK_VECTOR */
+static int __init mshv_sint_vector_setup(void)
+{
+       mshv_sint_vector = HYPERVISOR_CALLBACK_VECTOR;
+       return 0;
+}
+
+static void mshv_sint_vector_cleanup(void)
+{
+}
+#endif /* HYPERVISOR_CALLBACK_VECTOR */
+
 int __init mshv_synic_init(struct device *dev)
 {
        int ret = 0;
 
+       ret = mshv_sint_vector_setup();
+       if (ret)
+               return ret;
+
        synic_pages = alloc_percpu(struct hv_synic_pages);
        if (!synic_pages) {
                dev_err(dev, "Failed to allocate percpu synic page\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto sint_vector_cleanup;
        }
 
        ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
@@ -713,6 +809,8 @@ remove_cpuhp_state:
        cpuhp_remove_state(synic_cpuhp_online);
 free_synic_pages:
        free_percpu(synic_pages);
+sint_vector_cleanup:
+       mshv_sint_vector_cleanup();
        return ret;
 }
 
@@ -721,4 +819,5 @@ void mshv_synic_exit(void)
        unregister_reboot_notifier(&mshv_synic_reboot_nb);
        cpuhp_remove_state(synic_cpuhp_online);
        free_percpu(synic_pages);
+       mshv_sint_vector_cleanup();
 }
index 056ef7b6b36002808c389cde330cd835cd7ef70f..8bb3dd71c5b48f0b4c33c62c187f925bf0625df1 100644 (file)
@@ -1121,6 +1121,8 @@ enum hv_register_name {
        HV_X64_REGISTER_MSR_MTRR_FIX4KF8000     = 0x0008007A,
 
        HV_X64_REGISTER_REG_PAGE        = 0x0009001C,
+#elif defined(CONFIG_ARM64)
+       HV_ARM64_REGISTER_SINT_RESERVED_INTERRUPT_ID    = 0x00070001,
 #endif
 };