1 // SPDX-License-Identifier: GPL-2.0
4 * Hyper-V stub IOMMU driver.
6 * Copyright (C) 2019, Microsoft, Inc.
8 * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
11 #include <linux/types.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <linux/iommu.h>
15 #include <linux/module.h>
19 #include <asm/hw_irq.h>
20 #include <asm/io_apic.h>
21 #include <asm/irq_remapping.h>
22 #include <asm/hypervisor.h>
24 #include "irq_remapping.h"
26 #ifdef CONFIG_IRQ_REMAP
29 * According 82093AA IO-APIC spec , IO APIC has a 24-entry Interrupt
30 * Redirection Table. Hyper-V exposes one single IO-APIC and so define
31 * 24 IO APIC remmapping entries.
33 #define IOAPIC_REMAPPING_ENTRY 24
35 static cpumask_t ioapic_max_cpumask
= { CPU_BITS_NONE
};
36 static struct irq_domain
*ioapic_ir_domain
;
38 static int hyperv_ir_set_affinity(struct irq_data
*data
,
39 const struct cpumask
*mask
, bool force
)
41 struct irq_data
*parent
= data
->parent_data
;
42 struct irq_cfg
*cfg
= irqd_cfg(data
);
43 struct IO_APIC_route_entry
*entry
;
46 /* Return error If new irq affinity is out of ioapic_max_cpumask. */
47 if (!cpumask_subset(mask
, &ioapic_max_cpumask
))
50 ret
= parent
->chip
->irq_set_affinity(parent
, mask
, force
);
51 if (ret
< 0 || ret
== IRQ_SET_MASK_OK_DONE
)
54 entry
= data
->chip_data
;
55 entry
->dest
= cfg
->dest_apicid
;
56 entry
->vector
= cfg
->vector
;
57 send_cleanup_vector(cfg
);
62 static struct irq_chip hyperv_ir_chip
= {
64 .irq_ack
= apic_ack_irq
,
65 .irq_set_affinity
= hyperv_ir_set_affinity
,
68 static int hyperv_irq_remapping_alloc(struct irq_domain
*domain
,
69 unsigned int virq
, unsigned int nr_irqs
,
72 struct irq_alloc_info
*info
= arg
;
73 struct irq_data
*irq_data
;
74 struct irq_desc
*desc
;
77 if (!info
|| info
->type
!= X86_IRQ_ALLOC_TYPE_IOAPIC
|| nr_irqs
> 1)
80 ret
= irq_domain_alloc_irqs_parent(domain
, virq
, nr_irqs
, arg
);
84 irq_data
= irq_domain_get_irq_data(domain
, virq
);
86 irq_domain_free_irqs_common(domain
, virq
, nr_irqs
);
90 irq_data
->chip
= &hyperv_ir_chip
;
93 * If there is interrupt remapping function of IOMMU, setting irq
94 * affinity only needs to change IRTE of IOMMU. But Hyper-V doesn't
95 * support interrupt remapping function, setting irq affinity of IO-APIC
96 * interrupts still needs to change IO-APIC registers. But ioapic_
97 * configure_entry() will ignore value of cfg->vector and cfg->
98 * dest_apicid when IO-APIC's parent irq domain is not the vector
99 * domain.(See ioapic_configure_entry()) In order to setting vector
100 * and dest_apicid to IO-APIC register, IO-APIC entry pointer is saved
101 * in the chip_data and hyperv_irq_remapping_activate()/hyperv_ir_set_
102 * affinity() set vector and dest_apicid directly into IO-APIC entry.
104 irq_data
->chip_data
= info
->ioapic_entry
;
107 * Hypver-V IO APIC irq affinity should be in the scope of
108 * ioapic_max_cpumask because no irq remapping support.
110 desc
= irq_data_to_desc(irq_data
);
111 cpumask_copy(desc
->irq_common_data
.affinity
, &ioapic_max_cpumask
);
116 static void hyperv_irq_remapping_free(struct irq_domain
*domain
,
117 unsigned int virq
, unsigned int nr_irqs
)
119 irq_domain_free_irqs_common(domain
, virq
, nr_irqs
);
122 static int hyperv_irq_remapping_activate(struct irq_domain
*domain
,
123 struct irq_data
*irq_data
, bool reserve
)
125 struct irq_cfg
*cfg
= irqd_cfg(irq_data
);
126 struct IO_APIC_route_entry
*entry
= irq_data
->chip_data
;
128 entry
->dest
= cfg
->dest_apicid
;
129 entry
->vector
= cfg
->vector
;
134 static struct irq_domain_ops hyperv_ir_domain_ops
= {
135 .alloc
= hyperv_irq_remapping_alloc
,
136 .free
= hyperv_irq_remapping_free
,
137 .activate
= hyperv_irq_remapping_activate
,
140 static int __init
hyperv_prepare_irq_remapping(void)
142 struct fwnode_handle
*fn
;
145 if (!hypervisor_is_type(X86_HYPER_MS_HYPERV
) ||
149 fn
= irq_domain_alloc_named_id_fwnode("HYPERV-IR", 0);
154 irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
155 0, IOAPIC_REMAPPING_ENTRY
, fn
,
156 &hyperv_ir_domain_ops
, NULL
);
158 if (!ioapic_ir_domain
) {
159 irq_domain_free_fwnode(fn
);
164 * Hyper-V doesn't provide irq remapping function for
165 * IO-APIC and so IO-APIC only accepts 8-bit APIC ID.
166 * Cpu's APIC ID is read from ACPI MADT table and APIC IDs
167 * in the MADT table on Hyper-v are sorted monotonic increasingly.
168 * APIC ID reflects cpu topology. There maybe some APIC ID
169 * gaps when cpu number in a socket is not power of two. Prepare
170 * max cpu affinity for IOAPIC irqs. Scan cpu 0-255 and set cpu
171 * into ioapic_max_cpumask if its APIC ID is less than 256.
173 for (i
= min_t(unsigned int, num_possible_cpus() - 1, 255); i
>= 0; i
--)
174 if (cpu_physical_id(i
) < 256)
175 cpumask_set_cpu(i
, &ioapic_max_cpumask
);
180 static int __init
hyperv_enable_irq_remapping(void)
182 return IRQ_REMAP_X2APIC_MODE
;
185 static struct irq_domain
*hyperv_get_ir_irq_domain(struct irq_alloc_info
*info
)
187 if (info
->type
== X86_IRQ_ALLOC_TYPE_IOAPIC
)
188 return ioapic_ir_domain
;
193 struct irq_remap_ops hyperv_irq_remap_ops
= {
194 .prepare
= hyperv_prepare_irq_remapping
,
195 .enable
= hyperv_enable_irq_remapping
,
196 .get_ir_irq_domain
= hyperv_get_ir_irq_domain
,