162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/cpumask.h>
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci#include <linux/string.h>
562306a36Sopenharmony_ci#include <linux/errno.h>
662306a36Sopenharmony_ci#include <linux/msi.h>
762306a36Sopenharmony_ci#include <linux/irq.h>
862306a36Sopenharmony_ci#include <linux/pci.h>
962306a36Sopenharmony_ci#include <linux/irqdomain.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/hw_irq.h>
1262306a36Sopenharmony_ci#include <asm/irq_remapping.h>
1362306a36Sopenharmony_ci#include <asm/processor.h>
1462306a36Sopenharmony_ci#include <asm/x86_init.h>
1562306a36Sopenharmony_ci#include <asm/apic.h>
1662306a36Sopenharmony_ci#include <asm/hpet.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "irq_remapping.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint irq_remapping_enabled;
2162306a36Sopenharmony_ciint irq_remap_broken;
2262306a36Sopenharmony_ciint disable_sourceid_checking;
2362306a36Sopenharmony_ciint no_x2apic_optout;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint disable_irq_post = 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int disable_irq_remap;
2862306a36Sopenharmony_cistatic struct irq_remap_ops *remap_ops;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void irq_remapping_restore_boot_irq_mode(void)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	/*
3362306a36Sopenharmony_ci	 * With interrupt-remapping, for now we will use virtual wire A
3462306a36Sopenharmony_ci	 * mode, as virtual wire B is little complex (need to configure
3562306a36Sopenharmony_ci	 * both IOAPIC RTE as well as interrupt-remapping table entry).
3662306a36Sopenharmony_ci	 * As this gets called during crash dump, keep this simple for
3762306a36Sopenharmony_ci	 * now.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_APIC) || apic_from_smp_config())
4062306a36Sopenharmony_ci		disconnect_bsp_APIC(0);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void __init irq_remapping_modify_x86_ops(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	x86_apic_ops.restore = irq_remapping_restore_boot_irq_mode;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic __init int setup_nointremap(char *str)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	disable_irq_remap = 1;
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ciearly_param("nointremap", setup_nointremap);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic __init int setup_irqremap(char *str)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	if (!str)
5862306a36Sopenharmony_ci		return -EINVAL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	while (*str) {
6162306a36Sopenharmony_ci		if (!strncmp(str, "on", 2)) {
6262306a36Sopenharmony_ci			disable_irq_remap = 0;
6362306a36Sopenharmony_ci			disable_irq_post = 0;
6462306a36Sopenharmony_ci		} else if (!strncmp(str, "off", 3)) {
6562306a36Sopenharmony_ci			disable_irq_remap = 1;
6662306a36Sopenharmony_ci			disable_irq_post = 1;
6762306a36Sopenharmony_ci		} else if (!strncmp(str, "nosid", 5))
6862306a36Sopenharmony_ci			disable_sourceid_checking = 1;
6962306a36Sopenharmony_ci		else if (!strncmp(str, "no_x2apic_optout", 16))
7062306a36Sopenharmony_ci			no_x2apic_optout = 1;
7162306a36Sopenharmony_ci		else if (!strncmp(str, "nopost", 6))
7262306a36Sopenharmony_ci			disable_irq_post = 1;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		str += strcspn(str, ",");
7562306a36Sopenharmony_ci		while (*str == ',')
7662306a36Sopenharmony_ci			str++;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciearly_param("intremap", setup_irqremap);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid set_irq_remapping_broken(void)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	irq_remap_broken = 1;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cibool irq_remapping_cap(enum irq_remap_cap cap)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	if (!remap_ops || disable_irq_post)
9162306a36Sopenharmony_ci		return false;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return (remap_ops->capability & (1 << cap));
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(irq_remapping_cap);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint __init irq_remapping_prepare(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	if (disable_irq_remap)
10062306a36Sopenharmony_ci		return -ENOSYS;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_INTEL_IOMMU) &&
10362306a36Sopenharmony_ci	    intel_irq_remap_ops.prepare() == 0)
10462306a36Sopenharmony_ci		remap_ops = &intel_irq_remap_ops;
10562306a36Sopenharmony_ci	else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
10662306a36Sopenharmony_ci		 amd_iommu_irq_ops.prepare() == 0)
10762306a36Sopenharmony_ci		remap_ops = &amd_iommu_irq_ops;
10862306a36Sopenharmony_ci	else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) &&
10962306a36Sopenharmony_ci		 hyperv_irq_remap_ops.prepare() == 0)
11062306a36Sopenharmony_ci		remap_ops = &hyperv_irq_remap_ops;
11162306a36Sopenharmony_ci	else
11262306a36Sopenharmony_ci		return -ENOSYS;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint __init irq_remapping_enable(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int ret;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!remap_ops->enable)
12262306a36Sopenharmony_ci		return -ENODEV;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	ret = remap_ops->enable();
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (irq_remapping_enabled)
12762306a36Sopenharmony_ci		irq_remapping_modify_x86_ops();
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return ret;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_civoid irq_remapping_disable(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	if (irq_remapping_enabled && remap_ops->disable)
13562306a36Sopenharmony_ci		remap_ops->disable();
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciint irq_remapping_reenable(int mode)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	if (irq_remapping_enabled && remap_ops->reenable)
14162306a36Sopenharmony_ci		return remap_ops->reenable(mode);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint __init irq_remap_enable_fault_handling(void)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	if (!irq_remapping_enabled)
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!remap_ops->enable_faulting)
15262306a36Sopenharmony_ci		return -ENODEV;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return remap_ops->enable_faulting();
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_civoid panic_if_irq_remap(const char *msg)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	if (irq_remapping_enabled)
16062306a36Sopenharmony_ci		panic(msg);
16162306a36Sopenharmony_ci}
162