From: David Woodhouse <d...@amazon.co.uk>

Some hypervisors can allow the guest to use the Extended Destination ID
field in the IOAPIC RTE and MSI address to address up to 32768 CPUs.

Signed-off-by: David Woodhouse <d...@amazon.co.uk>
---
 arch/x86/include/asm/mpspec.h   |  1 +
 arch/x86/include/asm/x86_init.h |  2 ++
 arch/x86/kernel/apic/apic.c     | 15 ++++++++++++++-
 arch/x86/kernel/apic/msi.c      |  9 ++++++++-
 arch/x86/kernel/x86_init.c      |  1 +
 5 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
index e90ac7e9ae2c..25ee8ca0a1f2 100644
--- a/arch/x86/include/asm/mpspec.h
+++ b/arch/x86/include/asm/mpspec.h
@@ -42,6 +42,7 @@ extern DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
 extern unsigned int boot_cpu_physical_apicid;
 extern u8 boot_cpu_apic_version;
 extern unsigned long mp_lapic_addr;
+extern int msi_ext_dest_id;
 
 #ifdef CONFIG_X86_LOCAL_APIC
 extern int smp_found_config;
diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index 397196fae24d..5af3fe9e38f3 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -114,6 +114,7 @@ struct x86_init_pci {
  * @init_platform:             platform setup
  * @guest_late_init:           guest late init
  * @x2apic_available:          X2APIC detection
+ * @msi_ext_dest_id:           MSI and IOAPIC support 15-bit APIC IDs
  * @init_mem_mapping:          setup early mappings during init_mem_mapping()
  * @init_after_bootmem:                guest init after boot allocator is 
finished
  */
@@ -121,6 +122,7 @@ struct x86_hyper_init {
        void (*init_platform)(void);
        void (*guest_late_init)(void);
        bool (*x2apic_available)(void);
+       bool (*msi_ext_dest_id)(void);
        void (*init_mem_mapping)(void);
        void (*init_after_bootmem)(void);
 };
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index a75767052a92..459c78558f36 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1837,6 +1837,8 @@ static __init void x2apic_enable(void)
 
 static __init void try_to_enable_x2apic(int remap_mode)
 {
+       u32 apic_limit = 0;
+
        if (x2apic_state == X2APIC_DISABLED)
                return;
 
@@ -1858,7 +1860,15 @@ static __init void try_to_enable_x2apic(int remap_mode)
                                return;
                        }
 
-                       x2apic_set_max_apicid(255);
+                       /*
+                        * If the hypervisor supports extended destination ID
+                        * in IOAPIC and MSI, we can support that many CPUs.
+                        */
+                       if (x86_init.hyper.msi_ext_dest_id()) {
+                               msi_ext_dest_id = 1;
+                               apic_limit = 32767;
+                       } else
+                               apic_limit = 255;
                }
 
                /*
@@ -1867,6 +1877,9 @@ static __init void try_to_enable_x2apic(int remap_mode)
                 */
                x2apic_phys = 1;
        }
+       if (apic_limit)
+               x2apic_set_max_apicid(apic_limit);
+
        x2apic_enable();
 }
 
diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c
index 356f8acf4927..4d891967bea4 100644
--- a/arch/x86/kernel/apic/msi.c
+++ b/arch/x86/kernel/apic/msi.c
@@ -23,6 +23,8 @@
 
 struct irq_domain *x86_pci_msi_default_domain __ro_after_init;
 
+int msi_ext_dest_id __ro_after_init;
+
 static void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg, 
int dmar)
 {
        msg->address_hi = MSI_ADDR_BASE_HI;
@@ -45,10 +47,15 @@ static void __irq_msi_compose_msg(struct irq_cfg *cfg, 
struct msi_msg *msg, int
         * Only the IOMMU itself can use the trick of putting destination
         * APIC ID into the high bits of the address. Anything else would
         * just be writing to memory if it tried that, and needs IR to
-        * address APICs above 255.
+        * address APICs which can't be addressed in the normal 32-bit
+        * address range at 0xFFExxxxx. That is typically just 8 bits, but
+        * some hypervisors allow the extended destination ID field in bits
+        * 11-5 to be used, giving support for 15 bits of APIC IDs in total.
         */
        if (dmar)
                msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid);
+       else if (msi_ext_dest_id && cfg->dest_apicid < 0x8000)
+               msg->address_lo |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid) >> 3;
        else
                WARN_ON_ONCE(MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid));
 }
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index a3038d8deb6a..8b395821cb8d 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -110,6 +110,7 @@ struct x86_init_ops x86_init __initdata = {
                .init_platform          = x86_init_noop,
                .guest_late_init        = x86_init_noop,
                .x2apic_available       = bool_x86_init_noop,
+               .msi_ext_dest_id        = bool_x86_init_noop,
                .init_mem_mapping       = x86_init_noop,
                .init_after_bootmem     = x86_init_noop,
        },
-- 
2.26.2

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to