1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21c4248caSJoerg Roedel #include <linux/cpumask.h>
38a8f422dSSuresh Siddha #include <linux/kernel.h>
48a8f422dSSuresh Siddha #include <linux/string.h>
58a8f422dSSuresh Siddha #include <linux/errno.h>
698f1ad25SJoerg Roedel #include <linux/msi.h>
75afba62cSJoerg Roedel #include <linux/irq.h>
85afba62cSJoerg Roedel #include <linux/pci.h>
9a62b32cdSJiang Liu #include <linux/irqdomain.h>
1098f1ad25SJoerg Roedel
1198f1ad25SJoerg Roedel #include <asm/hw_irq.h>
1298f1ad25SJoerg Roedel #include <asm/irq_remapping.h>
131c4248caSJoerg Roedel #include <asm/processor.h>
141c4248caSJoerg Roedel #include <asm/x86_init.h>
151c4248caSJoerg Roedel #include <asm/apic.h>
165fc24d8cSYijing Wang #include <asm/hpet.h>
178a8f422dSSuresh Siddha
188a8f422dSSuresh Siddha #include "irq_remapping.h"
198a8f422dSSuresh Siddha
208a8f422dSSuresh Siddha int irq_remapping_enabled;
2103bbcb2eSNeil Horman int irq_remap_broken;
228a8f422dSSuresh Siddha int disable_sourceid_checking;
238a8f422dSSuresh Siddha int no_x2apic_optout;
248a8f422dSSuresh Siddha
25b7d20631SFeng Wu int disable_irq_post = 0;
263d9b98f4SFeng Wu
277fa1c842SJiang Liu static int disable_irq_remap;
288a8f422dSSuresh Siddha static struct irq_remap_ops *remap_ops;
298a8f422dSSuresh Siddha
irq_remapping_restore_boot_irq_mode(void)3051b146c5SBaoquan He static void irq_remapping_restore_boot_irq_mode(void)
311c4248caSJoerg Roedel {
321c4248caSJoerg Roedel /*
331c4248caSJoerg Roedel * With interrupt-remapping, for now we will use virtual wire A
341c4248caSJoerg Roedel * mode, as virtual wire B is little complex (need to configure
351c4248caSJoerg Roedel * both IOAPIC RTE as well as interrupt-remapping table entry).
361c4248caSJoerg Roedel * As this gets called during crash dump, keep this simple for
371c4248caSJoerg Roedel * now.
381c4248caSJoerg Roedel */
3993984fbdSBorislav Petkov if (boot_cpu_has(X86_FEATURE_APIC) || apic_from_smp_config())
401c4248caSJoerg Roedel disconnect_bsp_APIC(0);
411c4248caSJoerg Roedel }
421c4248caSJoerg Roedel
irq_remapping_modify_x86_ops(void)431c4248caSJoerg Roedel static void __init irq_remapping_modify_x86_ops(void)
441c4248caSJoerg Roedel {
4551b146c5SBaoquan He x86_apic_ops.restore = irq_remapping_restore_boot_irq_mode;
461c4248caSJoerg Roedel }
471c4248caSJoerg Roedel
setup_nointremap(char * str)488a8f422dSSuresh Siddha static __init int setup_nointremap(char *str)
498a8f422dSSuresh Siddha {
508a8f422dSSuresh Siddha disable_irq_remap = 1;
518a8f422dSSuresh Siddha return 0;
528a8f422dSSuresh Siddha }
538a8f422dSSuresh Siddha early_param("nointremap", setup_nointremap);
548a8f422dSSuresh Siddha
setup_irqremap(char * str)558a8f422dSSuresh Siddha static __init int setup_irqremap(char *str)
568a8f422dSSuresh Siddha {
578a8f422dSSuresh Siddha if (!str)
588a8f422dSSuresh Siddha return -EINVAL;
598a8f422dSSuresh Siddha
608a8f422dSSuresh Siddha while (*str) {
61b7d20631SFeng Wu if (!strncmp(str, "on", 2)) {
628a8f422dSSuresh Siddha disable_irq_remap = 0;
63b7d20631SFeng Wu disable_irq_post = 0;
64b7d20631SFeng Wu } else if (!strncmp(str, "off", 3)) {
658a8f422dSSuresh Siddha disable_irq_remap = 1;
66b7d20631SFeng Wu disable_irq_post = 1;
67b7d20631SFeng Wu } else if (!strncmp(str, "nosid", 5))
688a8f422dSSuresh Siddha disable_sourceid_checking = 1;
698a8f422dSSuresh Siddha else if (!strncmp(str, "no_x2apic_optout", 16))
708a8f422dSSuresh Siddha no_x2apic_optout = 1;
71b7d20631SFeng Wu else if (!strncmp(str, "nopost", 6))
72b7d20631SFeng Wu disable_irq_post = 1;
738a8f422dSSuresh Siddha
748a8f422dSSuresh Siddha str += strcspn(str, ",");
758a8f422dSSuresh Siddha while (*str == ',')
768a8f422dSSuresh Siddha str++;
778a8f422dSSuresh Siddha }
788a8f422dSSuresh Siddha
798a8f422dSSuresh Siddha return 0;
808a8f422dSSuresh Siddha }
818a8f422dSSuresh Siddha early_param("intremap", setup_irqremap);
828a8f422dSSuresh Siddha
set_irq_remapping_broken(void)8303bbcb2eSNeil Horman void set_irq_remapping_broken(void)
8403bbcb2eSNeil Horman {
8503bbcb2eSNeil Horman irq_remap_broken = 1;
8603bbcb2eSNeil Horman }
8703bbcb2eSNeil Horman
irq_remapping_cap(enum irq_remap_cap cap)88959c870fSFeng Wu bool irq_remapping_cap(enum irq_remap_cap cap)
89959c870fSFeng Wu {
90959c870fSFeng Wu if (!remap_ops || disable_irq_post)
9130e93761SJoerg Roedel return false;
92959c870fSFeng Wu
93959c870fSFeng Wu return (remap_ops->capability & (1 << cap));
94959c870fSFeng Wu }
95959c870fSFeng Wu EXPORT_SYMBOL_GPL(irq_remapping_cap);
96959c870fSFeng Wu
irq_remapping_prepare(void)978a8f422dSSuresh Siddha int __init irq_remapping_prepare(void)
988a8f422dSSuresh Siddha {
99c392f56cSJiang Liu if (disable_irq_remap)
100c392f56cSJiang Liu return -ENOSYS;
101c392f56cSJiang Liu
102*582a7ebaSBert Karwatzki if (IS_ENABLED(CONFIG_INTEL_IOMMU) &&
103*582a7ebaSBert Karwatzki intel_irq_remap_ops.prepare() == 0)
104a1dafe85SThomas Gleixner remap_ops = &intel_irq_remap_ops;
10530969e34SJiang Liu else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
10630969e34SJiang Liu amd_iommu_irq_ops.prepare() == 0)
107a1dafe85SThomas Gleixner remap_ops = &amd_iommu_irq_ops;
10829217a47SLan Tianyu else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) &&
10929217a47SLan Tianyu hyperv_irq_remap_ops.prepare() == 0)
11029217a47SLan Tianyu remap_ops = &hyperv_irq_remap_ops;
11130969e34SJiang Liu else
11230969e34SJiang Liu return -ENOSYS;
11330969e34SJiang Liu
114a1dafe85SThomas Gleixner return 0;
115a1dafe85SThomas Gleixner }
1168a8f422dSSuresh Siddha
irq_remapping_enable(void)1178a8f422dSSuresh Siddha int __init irq_remapping_enable(void)
1188a8f422dSSuresh Siddha {
1191c4248caSJoerg Roedel int ret;
1201c4248caSJoerg Roedel
121e9011760SJiang Liu if (!remap_ops->enable)
1228a8f422dSSuresh Siddha return -ENODEV;
1238a8f422dSSuresh Siddha
1241c4248caSJoerg Roedel ret = remap_ops->enable();
1251c4248caSJoerg Roedel
1261c4248caSJoerg Roedel if (irq_remapping_enabled)
1271c4248caSJoerg Roedel irq_remapping_modify_x86_ops();
1281c4248caSJoerg Roedel
1291c4248caSJoerg Roedel return ret;
1308a8f422dSSuresh Siddha }
1318a8f422dSSuresh Siddha
irq_remapping_disable(void)1328a8f422dSSuresh Siddha void irq_remapping_disable(void)
1338a8f422dSSuresh Siddha {
134e9011760SJiang Liu if (irq_remapping_enabled && remap_ops->disable)
1358a8f422dSSuresh Siddha remap_ops->disable();
1368a8f422dSSuresh Siddha }
1378a8f422dSSuresh Siddha
irq_remapping_reenable(int mode)1388a8f422dSSuresh Siddha int irq_remapping_reenable(int mode)
1398a8f422dSSuresh Siddha {
140e9011760SJiang Liu if (irq_remapping_enabled && remap_ops->reenable)
1418a8f422dSSuresh Siddha return remap_ops->reenable(mode);
142e9011760SJiang Liu
143e9011760SJiang Liu return 0;
1448a8f422dSSuresh Siddha }
1458a8f422dSSuresh Siddha
irq_remap_enable_fault_handling(void)1468a8f422dSSuresh Siddha int __init irq_remap_enable_fault_handling(void)
1478a8f422dSSuresh Siddha {
14870733e0cSJoerg Roedel if (!irq_remapping_enabled)
14970733e0cSJoerg Roedel return 0;
15070733e0cSJoerg Roedel
151e9011760SJiang Liu if (!remap_ops->enable_faulting)
1528a8f422dSSuresh Siddha return -ENODEV;
1538a8f422dSSuresh Siddha
1548a8f422dSSuresh Siddha return remap_ops->enable_faulting();
1558a8f422dSSuresh Siddha }
1568a8f422dSSuresh Siddha
panic_if_irq_remap(const char * msg)1576a9f5de2SJoerg Roedel void panic_if_irq_remap(const char *msg)
1586a9f5de2SJoerg Roedel {
1596a9f5de2SJoerg Roedel if (irq_remapping_enabled)
1606a9f5de2SJoerg Roedel panic(msg);
1616a9f5de2SJoerg Roedel }
162