root/drivers/iommu/hyperv-iommu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hyperv_ir_set_affinity
  2. hyperv_irq_remapping_alloc
  3. hyperv_irq_remapping_free
  4. hyperv_irq_remapping_activate
  5. hyperv_prepare_irq_remapping
  6. hyperv_enable_irq_remapping
  7. hyperv_get_ir_irq_domain

   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 
  35 static cpumask_t ioapic_max_cpumask = { CPU_BITS_NONE };
  36 static struct irq_domain *ioapic_ir_domain;
  37 
  38 static 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 
  62 static 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 
  68 static 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 
 116 static 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 
 122 static 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 
 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,
 138 };
 139 
 140 static 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 
 177 static int __init hyperv_enable_irq_remapping(void)
 178 {
 179         return IRQ_REMAP_X2APIC_MODE;
 180 }
 181 
 182 static 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 
 190 struct 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

/* [<][>][^][v][top][bottom][index][help] */