irq_remapping.c 4.25 KB
Newer Older
1
#include <linux/seq_file.h>
2
#include <linux/cpumask.h>
3 4 5
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
6
#include <linux/msi.h>
7 8
#include <linux/irq.h>
#include <linux/pci.h>
9
#include <linux/irqdomain.h>
10 11 12

#include <asm/hw_irq.h>
#include <asm/irq_remapping.h>
13 14 15
#include <asm/processor.h>
#include <asm/x86_init.h>
#include <asm/apic.h>
16
#include <asm/hpet.h>
17

18
#include "irq_remapping.h"
19

20
int irq_remapping_enabled;
21
int irq_remap_broken;
22 23 24
int disable_sourceid_checking;
int no_x2apic_optout;

25
int disable_irq_post = 0;
26

27
static int disable_irq_remap;
28 29
static struct irq_remap_ops *remap_ops;

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
static void irq_remapping_disable_io_apic(void)
{
	/*
	 * With interrupt-remapping, for now we will use virtual wire A
	 * mode, as virtual wire B is little complex (need to configure
	 * both IOAPIC RTE as well as interrupt-remapping table entry).
	 * As this gets called during crash dump, keep this simple for
	 * now.
	 */
	if (cpu_has_apic || apic_from_smp_config())
		disconnect_bsp_APIC(0);
}

static void __init irq_remapping_modify_x86_ops(void)
{
45
	x86_io_apic_ops.disable		= irq_remapping_disable_io_apic;
46 47
}

48 49
static __init int setup_nointremap(char *str)
{
50
	disable_irq_remap = 1;
51 52 53 54
	return 0;
}
early_param("nointremap", setup_nointremap);

55
static __init int setup_irqremap(char *str)
56 57 58 59 60
{
	if (!str)
		return -EINVAL;

	while (*str) {
61
		if (!strncmp(str, "on", 2)) {
62
			disable_irq_remap = 0;
63 64
			disable_irq_post = 0;
		} else if (!strncmp(str, "off", 3)) {
65
			disable_irq_remap = 1;
66 67
			disable_irq_post = 1;
		} else if (!strncmp(str, "nosid", 5))
68 69 70
			disable_sourceid_checking = 1;
		else if (!strncmp(str, "no_x2apic_optout", 16))
			no_x2apic_optout = 1;
71 72
		else if (!strncmp(str, "nopost", 6))
			disable_irq_post = 1;
73 74 75 76 77 78 79 80

		str += strcspn(str, ",");
		while (*str == ',')
			str++;
	}

	return 0;
}
81
early_param("intremap", setup_irqremap);
82

83 84 85 86 87
void set_irq_remapping_broken(void)
{
	irq_remap_broken = 1;
}

88 89 90
bool irq_remapping_cap(enum irq_remap_cap cap)
{
	if (!remap_ops || disable_irq_post)
91
		return false;
92 93 94 95 96

	return (remap_ops->capability & (1 << cap));
}
EXPORT_SYMBOL_GPL(irq_remapping_cap);

97
int __init irq_remapping_prepare(void)
98
{
99
	if (disable_irq_remap)
100
		return -ENOSYS;
101

102 103 104 105
	if (intel_irq_remap_ops.prepare() == 0)
		remap_ops = &intel_irq_remap_ops;
	else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
		 amd_iommu_irq_ops.prepare() == 0)
106
		remap_ops = &amd_iommu_irq_ops;
107 108 109 110
	else
		return -ENOSYS;

	return 0;
111 112
}

113
int __init irq_remapping_enable(void)
114
{
115 116
	int ret;

117
	if (!remap_ops->enable)
118 119
		return -ENODEV;

120 121 122 123 124 125
	ret = remap_ops->enable();

	if (irq_remapping_enabled)
		irq_remapping_modify_x86_ops();

	return ret;
126
}
127

128
void irq_remapping_disable(void)
129
{
130 131
	if (irq_remapping_enabled && remap_ops->disable)
		remap_ops->disable();
132 133
}

134
int irq_remapping_reenable(int mode)
135
{
136 137
	if (irq_remapping_enabled && remap_ops->reenable)
		return remap_ops->reenable(mode);
138

139
	return 0;
140 141
}

142
int __init irq_remap_enable_fault_handling(void)
143
{
144 145 146
	if (!irq_remapping_enabled)
		return 0;

147
	if (!remap_ops->enable_faulting)
148 149 150 151
		return -ENODEV;

	return remap_ops->enable_faulting();
}
152

153 154 155 156 157
void panic_if_irq_remap(const char *msg)
{
	if (irq_remapping_enabled)
		panic(msg);
}
158

159
void ir_ack_apic_edge(struct irq_data *data)
160 161 162 163
{
	ack_APIC_irq();
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
/**
 * irq_remapping_get_ir_irq_domain - Get the irqdomain associated with the IOMMU
 *				     device serving request @info
 * @info: interrupt allocation information, used to identify the IOMMU device
 *
 * It's used to get parent irqdomain for HPET and IOAPIC irqdomains.
 * Returns pointer to IRQ domain, or NULL on failure.
 */
struct irq_domain *
irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info)
{
	if (!remap_ops || !remap_ops->get_ir_irq_domain)
		return NULL;

	return remap_ops->get_ir_irq_domain(info);
}

/**
 * irq_remapping_get_irq_domain - Get the irqdomain serving the request @info
 * @info: interrupt allocation information, used to identify the IOMMU device
 *
 * There will be one PCI MSI/MSIX irqdomain associated with each interrupt
 * remapping device, so this interface is used to retrieve the PCI MSI/MSIX
 * irqdomain serving request @info.
 * Returns pointer to IRQ domain, or NULL on failure.
 */
struct irq_domain *
irq_remapping_get_irq_domain(struct irq_alloc_info *info)
{
	if (!remap_ops || !remap_ops->get_irq_domain)
		return NULL;

	return remap_ops->get_irq_domain(info);
}