summaryrefslogtreecommitdiff
path: root/arch/x86/kernel/apic/apic.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
-rw-r--r--arch/x86/kernel/apic/apic.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index f7196ee0f005..6bd20c0de8bc 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -94,6 +94,11 @@ static unsigned int disabled_cpu_apicid __ro_after_init = BAD_APICID;
static int apic_extnmi __ro_after_init = APIC_EXTNMI_BSP;
/*
+ * Hypervisor supports 15 bits of APIC ID in MSI Extended Destination ID
+ */
+static bool virt_ext_dest_id __ro_after_init;
+
+/*
* Map cpu index to physical APIC ID
*/
DEFINE_EARLY_PER_CPU_READ_MOSTLY(u16, x86_cpu_to_apicid, BAD_APICID);
@@ -1841,6 +1846,8 @@ static __init void try_to_enable_x2apic(int remap_mode)
return;
if (remap_mode != IRQ_REMAP_X2APIC_MODE) {
+ u32 apic_limit = 255;
+
/*
* Using X2APIC without IR is not architecturally supported
* on bare metal but may be supported in guests.
@@ -1852,11 +1859,21 @@ static __init void try_to_enable_x2apic(int remap_mode)
}
/*
+ * If the hypervisor supports extended destination ID in
+ * MSI, that increases the maximum APIC ID that can be
+ * used for non-remapped IRQ domains.
+ */
+ if (x86_init.hyper.msi_ext_dest_id()) {
+ virt_ext_dest_id = 1;
+ apic_limit = 32767;
+ }
+
+ /*
* Without IR, all CPUs can be addressed by IOAPIC/MSI only
* in physical mode, and CPUs with an APIC ID that cannnot
* be addressed must not be brought online.
*/
- x2apic_set_max_apicid(255);
+ x2apic_set_max_apicid(apic_limit);
x2apic_phys = 1;
}
x2apic_enable();
@@ -2497,10 +2514,15 @@ void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg,
* 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 higher APIC IDs.
+ * 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
+ * 5-11 to be used, giving support for 15 bits of APIC IDs in total.
*/
if (dmar)
msg->arch_addr_hi.destid_8_31 = cfg->dest_apicid >> 8;
+ else if (virt_ext_dest_id && cfg->dest_apicid < 0x8000)
+ msg->arch_addr_lo.virt_destid_8_14 = cfg->dest_apicid >> 8;
else
WARN_ON_ONCE(cfg->dest_apicid > 0xFF);
}