xref: /openbmc/linux/drivers/iommu/irq_remapping.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
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