[PART2 RFC v1 2/9] iommu/amd: Add data structure for guest vAPIC support
From: Suravee Suthikulpanit
Date: Fri Apr 08 2016 - 08:53:38 EST
From: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
This patch adds new data structure for the 128-bit IOMMU IRTE format,
which can support both legacy and GA interrupt remapping modes.
It also provides helper functions for setting up, accessing, and
updating interrupt remapping table entries in different mode.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
---
drivers/iommu/amd_iommu.c | 188 ++++++++++++++++++++++++++++++++--------
drivers/iommu/amd_iommu_types.h | 63 ++++++++++++++
2 files changed, 217 insertions(+), 34 deletions(-)
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 374c129..af6079a 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3601,21 +3601,6 @@ EXPORT_SYMBOL(amd_iommu_device_info);
*
*****************************************************************************/
-union irte {
- u32 val;
- struct {
- u32 valid : 1,
- no_fault : 1,
- int_type : 3,
- rq_eoi : 1,
- dm : 1,
- rsvd_1 : 1,
- destination : 8,
- vector : 8,
- rsvd_2 : 8;
- } fields;
-};
-
struct irq_2_irte {
u16 devid; /* Device ID for IRTE table */
u16 index; /* Index into IRTE table*/
@@ -3624,6 +3609,7 @@ struct irq_2_irte {
struct amd_ir_data {
struct irq_2_irte irq_2_irte;
union irte irte_entry;
+ struct irte_ga irte_ga_entry;
union {
struct msi_msg msi_entry;
};
@@ -3650,7 +3636,60 @@ static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
amd_iommu_dev_table[devid].data[2] = dte;
}
+void *amd_iommu_get_irte(struct irq_remap_table *table, int index)
+{
+ void *ret = NULL;
+
+ if (!amd_iommu_guest_ir) {
+ union irte *ptr = (union irte *)table->table;
+
+ ret = &ptr[index];
+ } else {
+ struct irte_ga *ptr = (struct irte_ga *)table->table;
+
+ ret = &ptr[index];
+ }
+ return ret;
+}
+
#define IRTE_ALLOCATED (~1U)
+static void set_irte_allocated(struct irq_remap_table *table, int index)
+{
+ if (!amd_iommu_guest_ir) {
+ table->table[index] = IRTE_ALLOCATED;
+ } else {
+ struct irte_ga *irte = amd_iommu_get_irte(table, index);
+
+ memset(&irte->lo.val, 0, sizeof(u64));
+ memset(&irte->hi.val, 0, sizeof(u64));
+ irte->hi.fields.vector = 0xff;
+ }
+}
+
+static bool is_irte_allocated(struct irq_remap_table *table, int index)
+{
+ if (!amd_iommu_guest_ir) {
+ union irte *irte = amd_iommu_get_irte(table, index);
+
+ return irte->val != 0;
+ } else {
+ struct irte_ga *irte = amd_iommu_get_irte(table, index);
+
+ return irte->hi.fields.vector != 0;
+ }
+}
+
+static void clear_irte(struct irq_remap_table *table, int index)
+{
+ if (!amd_iommu_guest_ir) {
+ table->table[index] = 0;
+ } else {
+ struct irte_ga *irte = amd_iommu_get_irte(table, index);
+
+ memset(&irte->lo.val, 0, sizeof(u64));
+ memset(&irte->hi.val, 0, sizeof(u64));
+ }
+}
static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
{
@@ -3697,13 +3736,18 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
goto out;
}
- memset(table->table, 0, MAX_IRQS_PER_TABLE * sizeof(u32));
+ if (!amd_iommu_guest_ir)
+ memset(table->table, 0,
+ MAX_IRQS_PER_TABLE * sizeof(u32));
+ else
+ memset(table->table, 0,
+ (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
if (ioapic) {
int i;
for (i = 0; i < 32; ++i)
- table->table[i] = IRTE_ALLOCATED;
+ set_irte_allocated(table, i);
}
irq_lookup_table[devid] = table;
@@ -3740,14 +3784,14 @@ static int alloc_irq_index(u16 devid, int count)
for (c = 0, index = table->min_index;
index < MAX_IRQS_PER_TABLE;
++index) {
- if (table->table[index] == 0)
+ if (!is_irte_allocated(table, index))
c += 1;
else
c = 0;
if (c == count) {
for (; c != 0; --c)
- table->table[index - c + 1] = IRTE_ALLOCATED;
+ set_irte_allocated(table, index - c + 1);
index -= count - 1;
goto out;
@@ -3762,6 +3806,42 @@ out:
return index;
}
+static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte)
+{
+ struct irq_remap_table *table;
+ struct amd_iommu *iommu;
+ unsigned long flags;
+ struct irte_ga *entry;
+ struct irte_ga tmp;
+
+ iommu = amd_iommu_rlookup_table[devid];
+ if (iommu == NULL)
+ return -EINVAL;
+
+ table = get_irq_table(devid, false);
+ if (!table)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&table->lock, flags);
+
+ entry = amd_iommu_get_irte(table, index);
+
+ memcpy(&tmp, entry, sizeof(struct irte_ga));
+
+ entry->lo.fields_remap.valid = 0;
+ entry->hi.val = irte->hi.val;
+ entry->hi.fields.ga_root_ptr = tmp.hi.fields.ga_root_ptr;
+ entry->lo.val = irte->lo.val;
+ entry->lo.fields_remap.valid = 1;
+
+ spin_unlock_irqrestore(&table->lock, flags);
+
+ iommu_flush_irt(iommu, devid);
+ iommu_completion_wait(iommu);
+
+ return 0;
+}
+
static int modify_irte(u16 devid, int index, union irte irte)
{
struct irq_remap_table *table;
@@ -3801,7 +3881,7 @@ static void free_irte(u16 devid, int index)
return;
spin_lock_irqsave(&table->lock, flags);
- table->table[index] = 0;
+ clear_irte(table, index);
spin_unlock_irqrestore(&table->lock, flags);
iommu_flush_irt(iommu, devid);
@@ -3889,19 +3969,33 @@ static void irq_remapping_prepare_irte(struct amd_ir_data *data,
{
struct irq_2_irte *irte_info = &data->irq_2_irte;
struct msi_msg *msg = &data->msi_entry;
- union irte *irte = &data->irte_entry;
struct IO_APIC_route_entry *entry;
data->irq_2_irte.devid = devid;
data->irq_2_irte.index = index + sub_handle;
/* Setup IRTE for IOMMU */
- irte->val = 0;
- irte->fields.vector = irq_cfg->vector;
- irte->fields.int_type = apic->irq_delivery_mode;
- irte->fields.destination = irq_cfg->dest_apicid;
- irte->fields.dm = apic->irq_dest_mode;
- irte->fields.valid = 1;
+ if (!amd_iommu_guest_ir) {
+ union irte *irte = &data->irte_entry;
+
+ irte->val = 0;
+ irte->fields.vector = irq_cfg->vector;
+ irte->fields.int_type = apic->irq_delivery_mode;
+ irte->fields.destination = irq_cfg->dest_apicid;
+ irte->fields.dm = apic->irq_dest_mode;
+ irte->fields.valid = 1;
+ } else {
+ struct irte_ga *irte = &data->irte_ga_entry;
+
+ irte->lo.val = 0;
+ irte->hi.val = 0;
+ irte->lo.fields_remap.guest_mode = 0;
+ irte->lo.fields_remap.int_type = apic->irq_delivery_mode;
+ irte->lo.fields_remap.dm = apic->irq_dest_mode;
+ irte->hi.fields.vector = irq_cfg->vector;
+ irte->lo.fields_remap.destination = irq_cfg->dest_apicid;
+ irte->lo.fields_remap.valid = 1;
+ }
switch (info->type) {
case X86_IRQ_ALLOC_TYPE_IOAPIC:
@@ -4037,7 +4131,13 @@ static void irq_remapping_activate(struct irq_domain *domain,
struct amd_ir_data *data = irq_data->chip_data;
struct irq_2_irte *irte_info = &data->irq_2_irte;
- modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+ if (!amd_iommu_guest_ir) {
+ data->irte_entry.fields.valid = 1;
+ modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+ } else if (amd_iommu_guest_ir >= AMD_IOMMU_GUEST_IR_LEGACY_GA) {
+ data->irte_ga_entry.lo.fields_remap.valid = 1;
+ modify_irte_ga(irte_info->devid, irte_info->index, &data->irte_ga_entry);
+ }
}
static void irq_remapping_deactivate(struct irq_domain *domain,
@@ -4045,10 +4145,14 @@ static void irq_remapping_deactivate(struct irq_domain *domain,
{
struct amd_ir_data *data = irq_data->chip_data;
struct irq_2_irte *irte_info = &data->irq_2_irte;
- union irte entry;
- entry.val = 0;
- modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+ if (!amd_iommu_guest_ir) {
+ data->irte_entry.fields.valid = 0;
+ modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+ } else {
+ data->irte_ga_entry.lo.fields_remap.valid = 0;
+ modify_irte_ga(irte_info->devid, irte_info->index, &data->irte_ga_entry);
+ }
}
static struct irq_domain_ops amd_ir_domain_ops = {
@@ -4075,9 +4179,19 @@ static int amd_ir_set_affinity(struct irq_data *data,
* Atomically updates the IRTE with the new destination, vector
* and flushes the interrupt entry cache.
*/
- ir_data->irte_entry.fields.vector = cfg->vector;
- ir_data->irte_entry.fields.destination = cfg->dest_apicid;
- modify_irte(irte_info->devid, irte_info->index, ir_data->irte_entry);
+ if (!amd_iommu_guest_ir) {
+ ir_data->irte_entry.fields.vector = cfg->vector;
+ ir_data->irte_entry.fields.destination = cfg->dest_apicid;
+ modify_irte(irte_info->devid, irte_info->index,
+ ir_data->irte_entry);
+ } else {
+ struct irte_ga *entry = &ir_data->irte_ga_entry;
+
+ entry->hi.fields.vector = cfg->vector;
+ entry->lo.fields_remap.destination = cfg->dest_apicid;
+ entry->lo.fields_remap.guest_mode = 0;
+ modify_irte_ga(irte_info->devid, irte_info->index, entry);
+ }
/*
* After this point, all the interrupts will start arriving
@@ -4097,19 +4211,25 @@ static void ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
}
static struct irq_chip amd_ir_chip = {
+ .name = "AMD-IR-IRQ-CHIP",
.irq_ack = ir_ack_apic_edge,
.irq_set_affinity = amd_ir_set_affinity,
.irq_compose_msi_msg = ir_compose_msi_msg,
};
+static const char amd_iommu_ir_domain_name[] = "AMD-IOMMU-IR-DOMAIN";
+static const char amd_iommu_msi_domain_name[] = "AMD-IOMMU-MSI-DOMAIN";
+
int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
{
iommu->ir_domain = irq_domain_add_tree(NULL, &amd_ir_domain_ops, iommu);
if (!iommu->ir_domain)
return -ENOMEM;
+ iommu->ir_domain->name = amd_iommu_ir_domain_name;
iommu->ir_domain->parent = arch_get_ir_parent_domain();
iommu->msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
+ iommu->msi_domain->name = amd_iommu_msi_domain_name;
return 0;
}
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 95414120..ec546f3 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -708,4 +708,67 @@ enum amd_iommu_intr_mode_type {
AMD_IOMMU_GUEST_IR_GA,
};
+union irte {
+ u32 val;
+ struct {
+ u32 valid : 1,
+ no_fault : 1,
+ int_type : 3,
+ rq_eoi : 1,
+ dm : 1,
+ rsvd_1 : 1,
+ destination : 8,
+ vector : 8,
+ rsvd_2 : 8;
+ } fields;
+};
+
+union irte_ga_lo {
+ u64 val;
+
+ /* For int remapping */
+ struct {
+ u64 valid : 1,
+ no_fault : 1,
+ /* ------ */
+ int_type : 3,
+ rq_eoi : 1,
+ dm : 1,
+ /* ------ */
+ guest_mode : 1,
+ destination : 8,
+ rsvd : 48;
+ } fields_remap;
+
+ /* For guest vAPIC */
+ struct {
+ u64 valid : 1,
+ no_fault : 1,
+ /* ------ */
+ ga_log_intr : 1,
+ rsvd1 : 3,
+ is_run : 1,
+ /* ------ */
+ guest_mode : 1,
+ destination : 8,
+ rsvd2 : 16,
+ ga_tag : 32;
+ } fields_vapic;
+};
+
+union irte_ga_hi {
+ u64 val;
+ struct {
+ u64 vector : 8,
+ rsvd_1 : 4,
+ ga_root_ptr : 40,
+ rsvd_2 : 12;
+ } fields;
+};
+
+struct irte_ga {
+ union irte_ga_lo lo;
+ union irte_ga_hi hi;
+};
+
#endif /* _ASM_X86_AMD_IOMMU_TYPES_H */
--
1.9.1