mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
iommu: translate IO-APIC pins when enabling interrupt remapping
On Intel hardware there's currently no translation of already enabled IO-APIC pins when interrupt remapping is enabled on the IOMMU, hence introduce a logic similar to the one used in x2apic_bsp_setup in order to save and mask all IO-APIC pins, and then translate and restore them after interrupt remapping has been enabled. With this change the AMD specific logic to deal with enabled pins (amd_iommu_setup_ioapic_remapping) can be removed, thus unifying the handling of IO-APIC when enabling interrupt remapping regardless of the IOMMU vendor. Reported-by: Andrew Cooper <andrew.cooper3@citrix.com> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> Release-acked-by: Juergen Gross <jgross@suse.com>
This commit is contained in:
1 parent
0e606c1bc0
commit
ecec150ab5
4 files changed
+40
-96
No files matched your search
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <xen/errno.h>
|
||||
#include <xen/acpi.h>
|
||||
#include <xen/keyhandler.h>
|
||||
#include <xen/pci.h>
|
||||
#include <xen/pci_regs.h>
|
||||
#include <xen/irq.h>
|
||||
@@ -1460,12 +1461,6 @@ int __init amd_iommu_init(bool xt)
|
||||
if ( rc )
|
||||
goto error_out;
|
||||
|
||||
/* initialize io-apic interrupt remapping entries */
|
||||
if ( iommu_intremap )
|
||||
rc = amd_iommu_setup_ioapic_remapping();
|
||||
if ( rc )
|
||||
goto error_out;
|
||||
|
||||
/* Allocate and initialize device table(s). */
|
||||
pci_init = !xt;
|
||||
rc = iterate_ivrs_mappings(amd_iommu_setup_device_table);
|
||||
@@ -1494,6 +1489,10 @@ int __init amd_iommu_init(bool xt)
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ( iommu_intremap )
|
||||
register_keyhandler('V', &amd_iommu_dump_intremap_tables,
|
||||
"dump IOMMU intremap tables", 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <asm/amd-iommu.h>
|
||||
#include <asm/hvm/svm/amd-iommu-proto.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <xen/keyhandler.h>
|
||||
#include <xen/softirq.h>
|
||||
|
||||
union irte32 {
|
||||
@@ -79,8 +78,6 @@ unsigned long *shared_intremap_inuse;
|
||||
static DEFINE_SPINLOCK(shared_intremap_lock);
|
||||
unsigned int nr_ioapic_sbdf;
|
||||
|
||||
static void dump_intremap_tables(unsigned char key);
|
||||
|
||||
#define intremap_page_order(irt) PFN_ORDER(virt_to_page(irt))
|
||||
|
||||
unsigned int amd_iommu_intremap_table_order(
|
||||
@@ -354,91 +351,6 @@ static int update_intremap_entry_from_ioapic(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init amd_iommu_setup_ioapic_remapping(void)
|
||||
{
|
||||
struct IO_APIC_route_entry rte;
|
||||
unsigned long flags;
|
||||
union irte_ptr entry;
|
||||
int apic, pin;
|
||||
u8 delivery_mode, dest, vector, dest_mode;
|
||||
u16 seg, bdf, req_id;
|
||||
struct amd_iommu *iommu;
|
||||
spinlock_t *lock;
|
||||
unsigned int offset;
|
||||
|
||||
/* Read ioapic entries and update interrupt remapping table accordingly */
|
||||
for ( apic = 0; apic < nr_ioapics; apic++ )
|
||||
{
|
||||
for ( pin = 0; pin < nr_ioapic_entries[apic]; pin++ )
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
rte = __ioapic_read_entry(apic, pin, 1);
|
||||
if ( rte.mask == 1 )
|
||||
continue;
|
||||
|
||||
/* get device id of ioapic devices */
|
||||
idx = ioapic_id_to_index(IO_APIC_ID(apic));
|
||||
if ( idx == MAX_IO_APICS )
|
||||
return -EINVAL;
|
||||
|
||||
bdf = ioapic_sbdf[idx].bdf;
|
||||
seg = ioapic_sbdf[idx].seg;
|
||||
iommu = find_iommu_for_device(seg, bdf);
|
||||
if ( !iommu )
|
||||
{
|
||||
AMD_IOMMU_DEBUG("Fail to find iommu for ioapic "
|
||||
"device id = %04x:%04x\n", seg, bdf);
|
||||
continue;
|
||||
}
|
||||
|
||||
req_id = get_intremap_requestor_id(iommu->seg, bdf);
|
||||
lock = get_intremap_lock(iommu->seg, req_id);
|
||||
|
||||
delivery_mode = rte.delivery_mode;
|
||||
vector = rte.vector;
|
||||
dest_mode = rte.dest_mode;
|
||||
dest = rte.dest.logical.logical_dest;
|
||||
|
||||
if ( iommu->ctrl.xt_en )
|
||||
{
|
||||
/*
|
||||
* In x2APIC mode we have no way of discovering the high 24
|
||||
* bits of the destination of an already enabled interrupt.
|
||||
* We come here earlier than for xAPIC mode, so no interrupts
|
||||
* should have been set up before.
|
||||
*/
|
||||
AMD_IOMMU_DEBUG("Unmasked IO-APIC#%u entry %u in x2APIC mode\n",
|
||||
IO_APIC_ID(apic), pin);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(lock, flags);
|
||||
offset = alloc_intremap_entry(iommu, req_id, 1);
|
||||
BUG_ON(offset >= INTREMAP_MAX_ENTRIES);
|
||||
entry = get_intremap_entry(iommu, req_id, offset);
|
||||
update_intremap_entry(iommu, entry, vector,
|
||||
delivery_mode, dest_mode, dest);
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
|
||||
set_rte_index(&rte, offset);
|
||||
ioapic_sbdf[idx].pin_2_idx[pin] = offset;
|
||||
__ioapic_write_entry(apic, pin, 1, rte);
|
||||
|
||||
if ( iommu->enabled )
|
||||
{
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
amd_iommu_flush_intremap(iommu, req_id);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_keyhandler('V', &dump_intremap_tables,
|
||||
"dump IOMMU intremap tables", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amd_iommu_ioapic_update_ire(
|
||||
unsigned int apic, unsigned int reg, unsigned int value)
|
||||
{
|
||||
@@ -982,7 +894,7 @@ static int dump_intremap_mapping(const struct amd_iommu *iommu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_intremap_tables(unsigned char key)
|
||||
void amd_iommu_dump_intremap_tables(unsigned char key)
|
||||
{
|
||||
if ( !shared_intremap_table )
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <xsm/xsm.h>
|
||||
|
||||
#include <asm/hvm/io.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
const struct iommu_init_ops *__initdata iommu_init_ops;
|
||||
@@ -28,6 +29,7 @@ struct iommu_ops __read_mostly iommu_ops;
|
||||
|
||||
int __init iommu_hardware_setup(void)
|
||||
{
|
||||
struct IO_APIC_route_entry **ioapic_entries = NULL;
|
||||
int rc;
|
||||
|
||||
if ( !iommu_init_ops )
|
||||
@@ -43,7 +45,37 @@ int __init iommu_hardware_setup(void)
|
||||
/* x2apic setup may have previously initialised the struct. */
|
||||
ASSERT(iommu_ops.init == iommu_init_ops->ops->init);
|
||||
|
||||
return iommu_init_ops->setup();
|
||||
if ( !x2apic_enabled && iommu_intremap )
|
||||
{
|
||||
/*
|
||||
* If x2APIC is enabled interrupt remapping is already enabled, so
|
||||
* there's no need to mess with the IO-APIC because the remapping
|
||||
* entries are already correctly setup by x2apic_bsp_setup.
|
||||
*/
|
||||
ioapic_entries = alloc_ioapic_entries();
|
||||
if ( !ioapic_entries )
|
||||
return -ENOMEM;
|
||||
rc = save_IO_APIC_setup(ioapic_entries);
|
||||
if ( rc )
|
||||
{
|
||||
free_ioapic_entries(ioapic_entries);
|
||||
return rc;
|
||||
}
|
||||
|
||||
mask_8259A();
|
||||
mask_IO_APIC_setup(ioapic_entries);
|
||||
}
|
||||
|
||||
rc = iommu_init_ops->setup();
|
||||
|
||||
if ( ioapic_entries )
|
||||
{
|
||||
restore_IO_APIC_setup(ioapic_entries, rc);
|
||||
unmask_8259A();
|
||||
free_ioapic_entries(ioapic_entries);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int iommu_enable_x2apic(void)
|
||||
|
||||
@@ -114,6 +114,7 @@ int amd_iommu_msi_msg_update_ire(
|
||||
void amd_iommu_read_msi_from_ire(
|
||||
struct msi_desc *msi_desc, struct msi_msg *msg);
|
||||
int amd_setup_hpet_msi(struct msi_desc *msi_desc);
|
||||
void amd_iommu_dump_intremap_tables(unsigned char key);
|
||||
|
||||
extern struct ioapic_sbdf {
|
||||
u16 bdf, seg;
|
||||
|
||||