linux/drivers/iommu/hyperv-iommu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/*
   4 * Hyper-V stub IOMMU driver.
   5 *
   6 * Copyright (C) 2019, Microsoft, Inc.
   7 *
   8 * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
   9 */
  10
  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>
  16
  17#include <asm/apic.h>
  18#include <asm/cpu.h>
  19#include <asm/hw_irq.h>
  20#include <asm/io_apic.h>
  21#include <asm/irq_remapping.h>
  22#include <asm/hypervisor.h>
  23
  24#include "irq_remapping.h"
  25
  26#ifdef CONFIG_IRQ_REMAP
  27
  28/*
  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.
  32 */
  33#define IOAPIC_REMAPPING_ENTRY 24
  34
  35static cpumask_t ioapic_max_cpumask = { CPU_BITS_NONE };
  36static struct irq_domain *ioapic_ir_domain;
  37
  38static int hyperv_ir_set_affinity(struct irq_data *data,
  39                const struct cpumask *mask, bool force)
  40{
  41        struct irq_data *parent = data->parent_data;
  42        struct irq_cfg *cfg = irqd_cfg(data);
  43        struct IO_APIC_route_entry *entry;
  44        int ret;
  45
  46        /* Return error If new irq affinity is out of ioapic_max_cpumask. */
  47        if (!cpumask_subset(mask, &ioapic_max_cpumask))
  48                return -EINVAL;
  49
  50        ret = parent->chip->irq_set_affinity(parent, mask, force);
  51        if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
  52                return ret;
  53
  54        entry = data->chip_data;
  55        entry->dest = cfg->dest_apicid;
  56        entry->vector = cfg->vector;
  57        send_cleanup_vector(cfg);
  58
  59        return 0;
  60}
  61
  62static struct irq_chip hyperv_ir_chip = {
  63        .name                   = "HYPERV-IR",
  64        .irq_ack                = apic_ack_irq,
  65        .irq_set_affinity       = hyperv_ir_set_affinity,
  66};
  67
  68static int hyperv_irq_remapping_alloc(struct irq_domain *domain,
  69                                     unsigned int virq, unsigned int nr_irqs,
  70                                     void *arg)
  71{
  72        struct irq_alloc_info *info = arg;
  73        struct irq_data *irq_data;
  74        struct irq_desc *desc;
  75        int ret = 0;
  76
  77        if (!info || info->type != X86_IRQ_ALLOC_TYPE_IOAPIC || nr_irqs > 1)
  78                return -EINVAL;
  79
  80        ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
  81        if (ret < 0)
  82                return ret;
  83
  84        irq_data = irq_domain_get_irq_data(domain, virq);
  85        if (!irq_data) {
  86                irq_domain_free_irqs_common(domain, virq, nr_irqs);
  87                return -EINVAL;
  88        }
  89
  90        irq_data->chip = &hyperv_ir_chip;
  91
  92        /*
  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.
 103         */
 104        irq_data->chip_data = info->ioapic_entry;
 105
 106        /*
 107         * Hypver-V IO APIC irq affinity should be in the scope of
 108         * ioapic_max_cpumask because no irq remapping support.
 109         */
 110        desc = irq_data_to_desc(irq_data);
 111        cpumask_copy(desc->irq_common_data.affinity, &ioapic_max_cpumask);
 112
 113        return 0;
 114}
 115
 116static void hyperv_irq_remapping_free(struct irq_domain *domain,
 117                                 unsigned int virq, unsigned int nr_irqs)
 118{
 119        irq_domain_free_irqs_common(domain, virq, nr_irqs);
 120}
 121
 122static int hyperv_irq_remapping_activate(struct irq_domain *domain,
 123                          struct irq_data *irq_data, bool reserve)
 124{
 125        struct irq_cfg *cfg = irqd_cfg(irq_data);
 126        struct IO_APIC_route_entry *entry = irq_data->chip_data;
 127
 128        entry->dest = cfg->dest_apicid;
 129        entry->vector = cfg->vector;
 130
 131        return 0;
 132}
 133
 134static 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,
 138};
 139
 140static int __init hyperv_prepare_irq_remapping(void)
 141{
 142        struct fwnode_handle *fn;
 143        int i;
 144
 145        if (!hypervisor_is_type(X86_HYPER_MS_HYPERV) ||
 146            !x2apic_supported())
 147                return -ENODEV;
 148
 149        fn = irq_domain_alloc_named_id_fwnode("HYPERV-IR", 0);
 150        if (!fn)
 151                return -ENOMEM;
 152
 153        ioapic_ir_domain =
 154                irq_domain_create_hierarchy(arch_get_ir_parent_domain(),
 155                                0, IOAPIC_REMAPPING_ENTRY, fn,
 156                                &hyperv_ir_domain_ops, NULL);
 157
 158        irq_domain_free_fwnode(fn);
 159
 160        /*
 161         * Hyper-V doesn't provide irq remapping function for
 162         * IO-APIC and so IO-APIC only accepts 8-bit APIC ID.
 163         * Cpu's APIC ID is read from ACPI MADT table and APIC IDs
 164         * in the MADT table on Hyper-v are sorted monotonic increasingly.
 165         * APIC ID reflects cpu topology. There maybe some APIC ID
 166         * gaps when cpu number in a socket is not power of two. Prepare
 167         * max cpu affinity for IOAPIC irqs. Scan cpu 0-255 and set cpu
 168         * into ioapic_max_cpumask if its APIC ID is less than 256.
 169         */
 170        for (i = min_t(unsigned int, num_possible_cpus() - 1, 255); i >= 0; i--)
 171                if (cpu_physical_id(i) < 256)
 172                        cpumask_set_cpu(i, &ioapic_max_cpumask);
 173
 174        return 0;
 175}
 176
 177static int __init hyperv_enable_irq_remapping(void)
 178{
 179        return IRQ_REMAP_X2APIC_MODE;
 180}
 181
 182static struct irq_domain *hyperv_get_ir_irq_domain(struct irq_alloc_info *info)
 183{
 184        if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC)
 185                return ioapic_ir_domain;
 186        else
 187                return NULL;
 188}
 189
 190struct irq_remap_ops hyperv_irq_remap_ops = {
 191        .prepare                = hyperv_prepare_irq_remapping,
 192        .enable                 = hyperv_enable_irq_remapping,
 193        .get_ir_irq_domain      = hyperv_get_ir_irq_domain,
 194};
 195
 196#endif
 197