1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun #include <linux/cpumask.h>
3*4882a593Smuzhiyun #include <linux/kernel.h>
4*4882a593Smuzhiyun #include <linux/string.h>
5*4882a593Smuzhiyun #include <linux/errno.h>
6*4882a593Smuzhiyun #include <linux/msi.h>
7*4882a593Smuzhiyun #include <linux/irq.h>
8*4882a593Smuzhiyun #include <linux/pci.h>
9*4882a593Smuzhiyun #include <linux/irqdomain.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <asm/hw_irq.h>
12*4882a593Smuzhiyun #include <asm/irq_remapping.h>
13*4882a593Smuzhiyun #include <asm/processor.h>
14*4882a593Smuzhiyun #include <asm/x86_init.h>
15*4882a593Smuzhiyun #include <asm/apic.h>
16*4882a593Smuzhiyun #include <asm/hpet.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include "irq_remapping.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun int irq_remapping_enabled;
21*4882a593Smuzhiyun int irq_remap_broken;
22*4882a593Smuzhiyun int disable_sourceid_checking;
23*4882a593Smuzhiyun int no_x2apic_optout;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun int disable_irq_post = 0;
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static int disable_irq_remap;
28*4882a593Smuzhiyun static struct irq_remap_ops *remap_ops;
29*4882a593Smuzhiyun
irq_remapping_restore_boot_irq_mode(void)30*4882a593Smuzhiyun static void irq_remapping_restore_boot_irq_mode(void)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun /*
33*4882a593Smuzhiyun * With interrupt-remapping, for now we will use virtual wire A
34*4882a593Smuzhiyun * mode, as virtual wire B is little complex (need to configure
35*4882a593Smuzhiyun * both IOAPIC RTE as well as interrupt-remapping table entry).
36*4882a593Smuzhiyun * As this gets called during crash dump, keep this simple for
37*4882a593Smuzhiyun * now.
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun if (boot_cpu_has(X86_FEATURE_APIC) || apic_from_smp_config())
40*4882a593Smuzhiyun disconnect_bsp_APIC(0);
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
irq_remapping_modify_x86_ops(void)43*4882a593Smuzhiyun static void __init irq_remapping_modify_x86_ops(void)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun x86_apic_ops.restore = irq_remapping_restore_boot_irq_mode;
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
setup_nointremap(char * str)48*4882a593Smuzhiyun static __init int setup_nointremap(char *str)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun disable_irq_remap = 1;
51*4882a593Smuzhiyun return 0;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun early_param("nointremap", setup_nointremap);
54*4882a593Smuzhiyun
setup_irqremap(char * str)55*4882a593Smuzhiyun static __init int setup_irqremap(char *str)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun if (!str)
58*4882a593Smuzhiyun return -EINVAL;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun while (*str) {
61*4882a593Smuzhiyun if (!strncmp(str, "on", 2)) {
62*4882a593Smuzhiyun disable_irq_remap = 0;
63*4882a593Smuzhiyun disable_irq_post = 0;
64*4882a593Smuzhiyun } else if (!strncmp(str, "off", 3)) {
65*4882a593Smuzhiyun disable_irq_remap = 1;
66*4882a593Smuzhiyun disable_irq_post = 1;
67*4882a593Smuzhiyun } else if (!strncmp(str, "nosid", 5))
68*4882a593Smuzhiyun disable_sourceid_checking = 1;
69*4882a593Smuzhiyun else if (!strncmp(str, "no_x2apic_optout", 16))
70*4882a593Smuzhiyun no_x2apic_optout = 1;
71*4882a593Smuzhiyun else if (!strncmp(str, "nopost", 6))
72*4882a593Smuzhiyun disable_irq_post = 1;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun str += strcspn(str, ",");
75*4882a593Smuzhiyun while (*str == ',')
76*4882a593Smuzhiyun str++;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun return 0;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun early_param("intremap", setup_irqremap);
82*4882a593Smuzhiyun
set_irq_remapping_broken(void)83*4882a593Smuzhiyun void set_irq_remapping_broken(void)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun irq_remap_broken = 1;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
irq_remapping_cap(enum irq_remap_cap cap)88*4882a593Smuzhiyun bool irq_remapping_cap(enum irq_remap_cap cap)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun if (!remap_ops || disable_irq_post)
91*4882a593Smuzhiyun return false;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun return (remap_ops->capability & (1 << cap));
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(irq_remapping_cap);
96*4882a593Smuzhiyun
irq_remapping_prepare(void)97*4882a593Smuzhiyun int __init irq_remapping_prepare(void)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun if (disable_irq_remap)
100*4882a593Smuzhiyun return -ENOSYS;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun if (intel_irq_remap_ops.prepare() == 0)
103*4882a593Smuzhiyun remap_ops = &intel_irq_remap_ops;
104*4882a593Smuzhiyun else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
105*4882a593Smuzhiyun amd_iommu_irq_ops.prepare() == 0)
106*4882a593Smuzhiyun remap_ops = &amd_iommu_irq_ops;
107*4882a593Smuzhiyun else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) &&
108*4882a593Smuzhiyun hyperv_irq_remap_ops.prepare() == 0)
109*4882a593Smuzhiyun remap_ops = &hyperv_irq_remap_ops;
110*4882a593Smuzhiyun else
111*4882a593Smuzhiyun return -ENOSYS;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
irq_remapping_enable(void)116*4882a593Smuzhiyun int __init irq_remapping_enable(void)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun int ret;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun if (!remap_ops->enable)
121*4882a593Smuzhiyun return -ENODEV;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun ret = remap_ops->enable();
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun if (irq_remapping_enabled)
126*4882a593Smuzhiyun irq_remapping_modify_x86_ops();
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return ret;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
irq_remapping_disable(void)131*4882a593Smuzhiyun void irq_remapping_disable(void)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun if (irq_remapping_enabled && remap_ops->disable)
134*4882a593Smuzhiyun remap_ops->disable();
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
irq_remapping_reenable(int mode)137*4882a593Smuzhiyun int irq_remapping_reenable(int mode)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun if (irq_remapping_enabled && remap_ops->reenable)
140*4882a593Smuzhiyun return remap_ops->reenable(mode);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
irq_remap_enable_fault_handling(void)145*4882a593Smuzhiyun int __init irq_remap_enable_fault_handling(void)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun if (!irq_remapping_enabled)
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun if (!remap_ops->enable_faulting)
151*4882a593Smuzhiyun return -ENODEV;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun return remap_ops->enable_faulting();
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
panic_if_irq_remap(const char * msg)156*4882a593Smuzhiyun void panic_if_irq_remap(const char *msg)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun if (irq_remapping_enabled)
159*4882a593Smuzhiyun panic(msg);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /**
163*4882a593Smuzhiyun * irq_remapping_get_irq_domain - Get the irqdomain serving the request @info
164*4882a593Smuzhiyun * @info: interrupt allocation information, used to identify the IOMMU device
165*4882a593Smuzhiyun *
166*4882a593Smuzhiyun * Returns pointer to IRQ domain, or NULL on failure.
167*4882a593Smuzhiyun */
irq_remapping_get_irq_domain(struct irq_alloc_info * info)168*4882a593Smuzhiyun struct irq_domain *irq_remapping_get_irq_domain(struct irq_alloc_info *info)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun if (!remap_ops || !remap_ops->get_irq_domain)
171*4882a593Smuzhiyun return NULL;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun return remap_ops->get_irq_domain(info);
174*4882a593Smuzhiyun }
175