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