mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
Re-enable MSI support
Currently the MSI is disabled because of some lock issue. This patch tries to clean up the locking related to MSI lock. Signed-off-by: Jiang Yunhong <yunhong.jiang@intel.com>
This commit is contained in:
1 parent
849bd717c2
commit
57b0566668
13 files changed
+302
-261
No files matched your search
+1
-16
@@ -665,14 +665,6 @@ long arch_do_domctl(
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
if ( device_assigned(bus, devfn) )
|
||||
{
|
||||
gdprintk(XENLOG_ERR, "XEN_DOMCTL_assign_device: "
|
||||
"%x:%x:%x already assigned, or non-existent\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
put_domain(d);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = assign_device(d, bus, devfn);
|
||||
if ( ret )
|
||||
@@ -715,15 +707,8 @@ long arch_do_domctl(
|
||||
put_domain(d);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !device_assigned(bus, devfn) )
|
||||
{
|
||||
put_domain(d);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
deassign_device(d, bus, devfn);
|
||||
ret = deassign_device(d, bus, devfn);
|
||||
gdprintk(XENLOG_INFO, "XEN_DOMCTL_deassign_device: bdf = %x:%x:%x\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
|
||||
|
||||
+38
-19
@@ -847,13 +847,12 @@ int map_domain_pirq(
|
||||
int old_vector, old_pirq;
|
||||
irq_desc_t *desc;
|
||||
unsigned long flags;
|
||||
struct msi_desc *msi_desc;
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
ASSERT(spin_is_locked(&d->event_lock));
|
||||
|
||||
/* XXX Until pcidev and msi locking is fixed. */
|
||||
if ( type == MAP_PIRQ_TYPE_MSI )
|
||||
return -EINVAL;
|
||||
|
||||
if ( !IS_PRIV(current->domain) )
|
||||
return -EPERM;
|
||||
|
||||
@@ -884,25 +883,35 @@ int map_domain_pirq(
|
||||
}
|
||||
|
||||
desc = &irq_desc[vector];
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
if ( type == MAP_PIRQ_TYPE_MSI )
|
||||
{
|
||||
struct msi_info *msi = (struct msi_info *)data;
|
||||
if ( desc->handler != &no_irq_type )
|
||||
dprintk(XENLOG_G_ERR, "dom%d: vector %d in use\n",
|
||||
d->domain_id, vector);
|
||||
desc->handler = &pci_msi_type;
|
||||
ret = pci_enable_msi(msi);
|
||||
|
||||
pdev = pci_get_pdev(msi->bus, msi->devfn);
|
||||
ret = pci_enable_msi(msi, &msi_desc);
|
||||
if ( ret )
|
||||
goto done;
|
||||
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
if ( desc->handler != &no_irq_type )
|
||||
dprintk(XENLOG_G_ERR, "dom%d: vector %d in use\n",
|
||||
d->domain_id, vector);
|
||||
desc->handler = &pci_msi_type;
|
||||
d->arch.pirq_vector[pirq] = vector;
|
||||
d->arch.vector_pirq[vector] = pirq;
|
||||
setup_msi_irq(pdev, msi_desc);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
} else
|
||||
{
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
d->arch.pirq_vector[pirq] = vector;
|
||||
d->arch.vector_pirq[vector] = pirq;
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
|
||||
d->arch.pirq_vector[pirq] = vector;
|
||||
d->arch.vector_pirq[vector] = pirq;
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -913,6 +922,7 @@ int unmap_domain_pirq(struct domain *d, int pirq)
|
||||
irq_desc_t *desc;
|
||||
int vector, ret = 0;
|
||||
bool_t forced_unbind;
|
||||
struct msi_desc *msi_desc = NULL;
|
||||
|
||||
if ( (pirq < 0) || (pirq >= NR_IRQS) )
|
||||
return -EINVAL;
|
||||
@@ -920,6 +930,7 @@ int unmap_domain_pirq(struct domain *d, int pirq)
|
||||
if ( !IS_PRIV(current->domain) )
|
||||
return -EINVAL;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
ASSERT(spin_is_locked(&d->event_lock));
|
||||
|
||||
vector = d->arch.pirq_vector[pirq];
|
||||
@@ -937,18 +948,19 @@ int unmap_domain_pirq(struct domain *d, int pirq)
|
||||
d->domain_id, pirq);
|
||||
|
||||
desc = &irq_desc[vector];
|
||||
|
||||
if ( (msi_desc = desc->msi_desc) != NULL )
|
||||
pci_disable_msi(msi_desc);
|
||||
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
BUG_ON(vector != d->arch.pirq_vector[pirq]);
|
||||
|
||||
if ( desc->msi_desc )
|
||||
pci_disable_msi(vector);
|
||||
if ( msi_desc )
|
||||
teardown_msi_vector(vector);
|
||||
|
||||
if ( desc->handler == &pci_msi_type )
|
||||
{
|
||||
desc->handler = &no_irq_type;
|
||||
free_irq_vector(vector);
|
||||
}
|
||||
|
||||
if ( !forced_unbind )
|
||||
{
|
||||
@@ -962,6 +974,11 @@ int unmap_domain_pirq(struct domain *d, int pirq)
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
if (msi_desc)
|
||||
{
|
||||
msi_free_vector(msi_desc);
|
||||
free_irq_vector(vector);
|
||||
}
|
||||
|
||||
ret = irq_deny_access(d, pirq);
|
||||
if ( ret )
|
||||
@@ -976,6 +993,7 @@ void free_domain_pirqs(struct domain *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
spin_lock(&d->event_lock);
|
||||
|
||||
for ( i = 0; i < NR_IRQS; i++ )
|
||||
@@ -983,6 +1001,7 @@ void free_domain_pirqs(struct domain *d)
|
||||
unmap_domain_pirq(d, i);
|
||||
|
||||
spin_unlock(&d->event_lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
}
|
||||
|
||||
extern void dump_ioapic_irq_info(void);
|
||||
|
||||
+62
-87
@@ -153,6 +153,8 @@ static int set_vector_msi(struct msi_desc *entry)
|
||||
|
||||
static int unset_vector_msi(int vector)
|
||||
{
|
||||
ASSERT(spin_is_locked(&irq_desc[vector].lock));
|
||||
|
||||
if ( vector >= NR_VECTORS )
|
||||
{
|
||||
dprintk(XENLOG_ERR, "Trying to uninstall msi data for Vector %d\n",
|
||||
@@ -161,6 +163,7 @@ static int unset_vector_msi(int vector)
|
||||
}
|
||||
|
||||
irq_desc[vector].msi_desc = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -228,14 +231,12 @@ void set_msi_affinity(unsigned int vector, cpumask_t mask)
|
||||
return;
|
||||
|
||||
ASSERT(spin_is_locked(&irq_desc[vector].lock));
|
||||
spin_lock(&desc->dev->lock);
|
||||
read_msi_msg(desc, &msg);
|
||||
|
||||
msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
|
||||
msg.address_lo |= MSI_ADDR_DEST_ID(dest);
|
||||
|
||||
write_msi_msg(desc, &msg);
|
||||
spin_unlock(&desc->dev->lock);
|
||||
}
|
||||
|
||||
static void msi_set_enable(struct pci_dev *dev, int enable)
|
||||
@@ -369,7 +370,7 @@ static struct msi_desc* alloc_msi_entry(void)
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
|
||||
int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
|
||||
{
|
||||
struct msi_msg msg;
|
||||
|
||||
@@ -380,19 +381,13 @@ static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown_msi_vector(int vector)
|
||||
void teardown_msi_vector(int vector)
|
||||
{
|
||||
unset_vector_msi(vector);
|
||||
}
|
||||
|
||||
static void msi_free_vector(int vector)
|
||||
int msi_free_vector(struct msi_desc *entry)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
|
||||
ASSERT(spin_is_locked(&irq_desc[vector].lock));
|
||||
entry = irq_desc[vector].msi_desc;
|
||||
teardown_msi_vector(vector);
|
||||
|
||||
if ( entry->msi_attrib.type == PCI_CAP_ID_MSIX )
|
||||
{
|
||||
unsigned long start;
|
||||
@@ -407,6 +402,7 @@ static void msi_free_vector(int vector)
|
||||
}
|
||||
list_del(&entry->list);
|
||||
xfree(entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct msi_desc *find_msi_entry(struct pci_dev *dev,
|
||||
@@ -433,15 +429,18 @@ static struct msi_desc *find_msi_entry(struct pci_dev *dev,
|
||||
* multiple messages. A return of zero indicates the successful setup
|
||||
* of an entry zero with the new MSI irq or non-zero for otherwise.
|
||||
**/
|
||||
static int msi_capability_init(struct pci_dev *dev, int vector)
|
||||
static int msi_capability_init(struct pci_dev *dev,
|
||||
int vector,
|
||||
struct msi_desc **desc)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
int pos, ret;
|
||||
int pos;
|
||||
u16 control;
|
||||
u8 bus = dev->bus;
|
||||
u8 slot = PCI_SLOT(dev->devfn);
|
||||
u8 func = PCI_FUNC(dev->devfn);
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSI);
|
||||
control = pci_conf_read16(bus, slot, func, msi_control_reg(pos));
|
||||
/* MSI Entry Initialization */
|
||||
@@ -477,14 +476,7 @@ static int msi_capability_init(struct pci_dev *dev, int vector)
|
||||
}
|
||||
list_add_tail(&entry->list, &dev->msi_list);
|
||||
|
||||
/* Configure MSI capability structure */
|
||||
ret = setup_msi_irq(dev, entry);
|
||||
if ( ret )
|
||||
{
|
||||
msi_free_vector(vector);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*desc = entry;
|
||||
/* Restore the original MSI enabled bits */
|
||||
pci_conf_write16(bus, slot, func, msi_control_reg(pos), control);
|
||||
|
||||
@@ -501,7 +493,9 @@ static int msi_capability_init(struct pci_dev *dev, int vector)
|
||||
* single MSI-X irq. A return of zero indicates the successful setup of
|
||||
* requested MSI-X entries with allocated irqs or non-zero for otherwise.
|
||||
**/
|
||||
static int msix_capability_init(struct pci_dev *dev, struct msi_info *msi)
|
||||
static int msix_capability_init(struct pci_dev *dev,
|
||||
struct msi_info *msi,
|
||||
struct msi_desc **desc)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
int pos;
|
||||
@@ -515,6 +509,9 @@ static int msix_capability_init(struct pci_dev *dev, struct msi_info *msi)
|
||||
u8 slot = PCI_SLOT(dev->devfn);
|
||||
u8 func = PCI_FUNC(dev->devfn);
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
ASSERT(desc);
|
||||
|
||||
pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
|
||||
control = pci_conf_read16(bus, slot, func, msix_control_reg(pos));
|
||||
msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
|
||||
@@ -550,9 +547,13 @@ static int msix_capability_init(struct pci_dev *dev, struct msi_info *msi)
|
||||
|
||||
list_add_tail(&entry->list, &dev->msi_list);
|
||||
|
||||
setup_msi_irq(dev, entry);
|
||||
/* Mask interrupt here */
|
||||
writel(1, entry->mask_base + entry->msi_attrib.entry_nr
|
||||
* PCI_MSIX_ENTRY_SIZE
|
||||
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
|
||||
|
||||
/* Set MSI-X enabled bits */
|
||||
*desc = entry;
|
||||
/* Restore MSI-X enabled bits */
|
||||
pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
|
||||
|
||||
return 0;
|
||||
@@ -568,45 +569,35 @@ static int msix_capability_init(struct pci_dev *dev, struct msi_info *msi)
|
||||
* indicates the successful setup of an entry zero with the new MSI
|
||||
* irq or non-zero for otherwise.
|
||||
**/
|
||||
static int __pci_enable_msi(struct msi_info *msi)
|
||||
static int __pci_enable_msi(struct msi_info *msi, struct msi_desc **desc)
|
||||
{
|
||||
int status;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
pdev = pci_lock_pdev(msi->bus, msi->devfn);
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
pdev = pci_get_pdev(msi->bus, msi->devfn);
|
||||
if ( !pdev )
|
||||
return -ENODEV;
|
||||
|
||||
if ( find_msi_entry(pdev, msi->vector, PCI_CAP_ID_MSI) )
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
dprintk(XENLOG_WARNING, "vector %d has already mapped to MSI on "
|
||||
"device %02x:%02x.%01x.\n", msi->vector, msi->bus,
|
||||
PCI_SLOT(msi->devfn), PCI_FUNC(msi->devfn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = msi_capability_init(pdev, msi->vector);
|
||||
spin_unlock(&pdev->lock);
|
||||
status = msi_capability_init(pdev, msi->vector, desc);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void __pci_disable_msi(int vector)
|
||||
static void __pci_disable_msi(struct msi_desc *entry)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
struct pci_dev *dev;
|
||||
int pos;
|
||||
u16 control;
|
||||
u8 bus, slot, func;
|
||||
|
||||
entry = irq_desc[vector].msi_desc;
|
||||
if ( !entry )
|
||||
return;
|
||||
/*
|
||||
* Lock here is safe. msi_desc can not be removed without holding
|
||||
* both irq_desc[].lock (which we do) and pdev->lock.
|
||||
*/
|
||||
spin_lock(&entry->dev->lock);
|
||||
dev = entry->dev;
|
||||
bus = dev->bus;
|
||||
slot = PCI_SLOT(dev->devfn);
|
||||
@@ -618,10 +609,6 @@ static void __pci_disable_msi(int vector)
|
||||
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
|
||||
msi_free_vector(vector);
|
||||
|
||||
pci_conf_write16(bus, slot, func, msi_control_reg(pos), control);
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -639,7 +626,7 @@ static void __pci_disable_msi(int vector)
|
||||
* of irqs available. Driver should use the returned value to re-send
|
||||
* its request.
|
||||
**/
|
||||
static int __pci_enable_msix(struct msi_info *msi)
|
||||
static int __pci_enable_msix(struct msi_info *msi, struct msi_desc **desc)
|
||||
{
|
||||
int status, pos, nr_entries;
|
||||
struct pci_dev *pdev;
|
||||
@@ -647,7 +634,8 @@ static int __pci_enable_msix(struct msi_info *msi)
|
||||
u8 slot = PCI_SLOT(msi->devfn);
|
||||
u8 func = PCI_FUNC(msi->devfn);
|
||||
|
||||
pdev = pci_lock_pdev(msi->bus, msi->devfn);
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
pdev = pci_get_pdev(msi->bus, msi->devfn);
|
||||
if ( !pdev )
|
||||
return -ENODEV;
|
||||
|
||||
@@ -655,41 +643,27 @@ static int __pci_enable_msix(struct msi_info *msi)
|
||||
control = pci_conf_read16(msi->bus, slot, func, msi_control_reg(pos));
|
||||
nr_entries = multi_msix_capable(control);
|
||||
if (msi->entry_nr >= nr_entries)
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ( find_msi_entry(pdev, msi->vector, PCI_CAP_ID_MSIX) )
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
dprintk(XENLOG_WARNING, "vector %d has already mapped to MSIX on "
|
||||
"device %02x:%02x.%01x.\n", msi->vector, msi->bus,
|
||||
PCI_SLOT(msi->devfn), PCI_FUNC(msi->devfn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = msix_capability_init(pdev, msi);
|
||||
spin_unlock(&pdev->lock);
|
||||
status = msix_capability_init(pdev, msi, desc);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void __pci_disable_msix(int vector)
|
||||
static void __pci_disable_msix(struct msi_desc *entry)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
struct pci_dev *dev;
|
||||
int pos;
|
||||
u16 control;
|
||||
u8 bus, slot, func;
|
||||
|
||||
entry = irq_desc[vector].msi_desc;
|
||||
if ( !entry )
|
||||
return;
|
||||
/*
|
||||
* Lock here is safe. msi_desc can not be removed without holding
|
||||
* both irq_desc[].lock (which we do) and pdev->lock.
|
||||
*/
|
||||
spin_lock(&entry->dev->lock);
|
||||
dev = entry->dev;
|
||||
bus = dev->bus;
|
||||
slot = PCI_SLOT(dev->devfn);
|
||||
@@ -701,50 +675,51 @@ static void __pci_disable_msix(int vector)
|
||||
|
||||
BUG_ON(list_empty(&dev->msi_list));
|
||||
|
||||
msi_free_vector(vector);
|
||||
writel(1, entry->mask_base + entry->msi_attrib.entry_nr
|
||||
* PCI_MSIX_ENTRY_SIZE
|
||||
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
|
||||
|
||||
pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
int pci_enable_msi(struct msi_info *msi)
|
||||
/*
|
||||
* Notice: only construct the msi_desc
|
||||
* no change to irq_desc here, and the interrupt is masked
|
||||
*/
|
||||
int pci_enable_msi(struct msi_info *msi, struct msi_desc **desc)
|
||||
{
|
||||
ASSERT(spin_is_locked(&irq_desc[msi->vector].lock));
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
return msi->table_base ? __pci_enable_msix(msi) :
|
||||
__pci_enable_msi(msi);
|
||||
return msi->table_base ? __pci_enable_msix(msi, desc) :
|
||||
__pci_enable_msi(msi, desc);
|
||||
}
|
||||
|
||||
void pci_disable_msi(int vector)
|
||||
/*
|
||||
* Device only, no irq_desc
|
||||
*/
|
||||
void pci_disable_msi(struct msi_desc *msi_desc)
|
||||
{
|
||||
irq_desc_t *desc = &irq_desc[vector];
|
||||
ASSERT(spin_is_locked(&desc->lock));
|
||||
if ( !desc->msi_desc )
|
||||
return;
|
||||
|
||||
if ( desc->msi_desc->msi_attrib.type == PCI_CAP_ID_MSI )
|
||||
__pci_disable_msi(vector);
|
||||
else if ( desc->msi_desc->msi_attrib.type == PCI_CAP_ID_MSIX )
|
||||
__pci_disable_msix(vector);
|
||||
if ( msi_desc->msi_attrib.type == PCI_CAP_ID_MSI )
|
||||
__pci_disable_msi(msi_desc);
|
||||
else if ( msi_desc->msi_attrib.type == PCI_CAP_ID_MSIX )
|
||||
__pci_disable_msix(msi_desc);
|
||||
}
|
||||
|
||||
static void msi_free_vectors(struct pci_dev* dev)
|
||||
{
|
||||
struct msi_desc *entry, *tmp;
|
||||
irq_desc_t *desc;
|
||||
unsigned long flags;
|
||||
unsigned long flags, vector;
|
||||
|
||||
retry:
|
||||
list_for_each_entry_safe( entry, tmp, &dev->msi_list, list )
|
||||
{
|
||||
desc = &irq_desc[entry->vector];
|
||||
vector = entry->vector;
|
||||
desc = &irq_desc[vector];
|
||||
pci_disable_msi(entry);
|
||||
|
||||
local_irq_save(flags);
|
||||
if ( !spin_trylock(&desc->lock) )
|
||||
{
|
||||
local_irq_restore(flags);
|
||||
goto retry;
|
||||
}
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
teardown_msi_vector(vector);
|
||||
|
||||
if ( desc->handler == &pci_msi_type )
|
||||
{
|
||||
@@ -753,8 +728,8 @@ static void msi_free_vectors(struct pci_dev* dev)
|
||||
desc->handler = &no_irq_type;
|
||||
}
|
||||
|
||||
msi_free_vector(entry->vector);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
msi_free_vector(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ static int physdev_map_pirq(struct physdev_map_pirq *map)
|
||||
goto free_domain;
|
||||
}
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
/* Verify or get pirq. */
|
||||
spin_lock(&d->event_lock);
|
||||
if ( map->pirq < 0 )
|
||||
@@ -147,6 +148,7 @@ static int physdev_map_pirq(struct physdev_map_pirq *map)
|
||||
|
||||
done:
|
||||
spin_unlock(&d->event_lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
if ( (ret != 0) && (map->type == MAP_PIRQ_TYPE_MSI) && (map->index == -1) )
|
||||
free_irq_vector(vector);
|
||||
free_domain:
|
||||
@@ -170,9 +172,11 @@ static int physdev_unmap_pirq(struct physdev_unmap_pirq *unmap)
|
||||
if ( d == NULL )
|
||||
return -ESRCH;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
spin_lock(&d->event_lock);
|
||||
ret = unmap_domain_pirq(d, unmap->pirq);
|
||||
spin_unlock(&d->event_lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
|
||||
rcu_unlock_domain(d);
|
||||
|
||||
@@ -341,10 +345,12 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
|
||||
|
||||
irq_op.vector = assign_irq_vector(irq);
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
spin_lock(&dom0->event_lock);
|
||||
ret = map_domain_pirq(dom0, irq_op.irq, irq_op.vector,
|
||||
MAP_PIRQ_TYPE_GSI, NULL);
|
||||
spin_unlock(&dom0->event_lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
|
||||
if ( copy_to_guest(arg, &irq_op, 1) != 0 )
|
||||
ret = -EFAULT;
|
||||
|
||||
@@ -60,6 +60,8 @@ void __dummy__(void)
|
||||
DEFINE(UREGS_user_sizeof, sizeof(struct cpu_user_regs));
|
||||
BLANK();
|
||||
|
||||
OFFSET(irq_caps_offset, struct domain, irq_caps);
|
||||
OFFSET(next_in_list_offset, struct domain, next_in_list);
|
||||
OFFSET(VCPU_processor, struct vcpu, processor);
|
||||
OFFSET(VCPU_domain, struct vcpu, domain);
|
||||
OFFSET(VCPU_vcpu_info, struct vcpu, vcpu_info);
|
||||
|
||||
+2
-2
@@ -558,12 +558,12 @@ static void complete_domain_destroy(struct rcu_head *head)
|
||||
sched_destroy_vcpu(v);
|
||||
}
|
||||
|
||||
rangeset_domain_destroy(d);
|
||||
|
||||
grant_table_destroy(d);
|
||||
|
||||
arch_domain_destroy(d);
|
||||
|
||||
rangeset_domain_destroy(d);
|
||||
|
||||
sched_destroy_domain(d);
|
||||
|
||||
/* Free page used by xen oprofile buffer. */
|
||||
|
||||
@@ -282,9 +282,13 @@ static int reassign_device( struct domain *source, struct domain *target,
|
||||
struct amd_iommu *iommu;
|
||||
int bdf;
|
||||
|
||||
pdev = pci_lock_domain_pdev(source, bus, devfn);
|
||||
read_lock(&pcidevs_lock);
|
||||
pdev = pci_get_pdev_by_domain(source, bus, devfn);
|
||||
if ( !pdev )
|
||||
return -ENODEV;
|
||||
{
|
||||
read_unlock(&pcidevs_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bdf = (bus << 8) | devfn;
|
||||
/* supported device? */
|
||||
@@ -293,8 +297,8 @@ static int reassign_device( struct domain *source, struct domain *target,
|
||||
|
||||
if ( !iommu )
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
amd_iov_error("Fail to find iommu."
|
||||
read_unlock(&pcidevs_lock);
|
||||
amd_iov_error("Fail to find iommu."
|
||||
" %x:%x.%x cannot be assigned to domain %d\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn), target->domain_id);
|
||||
return -ENODEV;
|
||||
@@ -302,9 +306,7 @@ static int reassign_device( struct domain *source, struct domain *target,
|
||||
|
||||
amd_iommu_disable_domain_device(source, iommu, bdf);
|
||||
|
||||
write_lock(&pcidevs_lock);
|
||||
list_move(&pdev->domain_list, &target->arch.pdev_list);
|
||||
write_unlock(&pcidevs_lock);
|
||||
pdev->domain = target;
|
||||
|
||||
amd_iommu_setup_domain_device(target, iommu, bdf);
|
||||
@@ -312,7 +314,7 @@ static int reassign_device( struct domain *source, struct domain *target,
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
||||
source->domain_id, target->domain_id);
|
||||
|
||||
spin_unlock(&pdev->lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,12 @@ int iommu_domain_init(struct domain *domain)
|
||||
int iommu_add_device(struct pci_dev *pdev)
|
||||
{
|
||||
struct hvm_iommu *hd;
|
||||
|
||||
if ( !pdev->domain )
|
||||
return -EINVAL;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
hd = domain_hvm_iommu(pdev->domain);
|
||||
if ( !iommu_enabled || !hd->platform_ops )
|
||||
return 0;
|
||||
@@ -109,20 +112,24 @@ int iommu_remove_device(struct pci_dev *pdev)
|
||||
int assign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
{
|
||||
struct hvm_iommu *hd = domain_hvm_iommu(d);
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
if ( !iommu_enabled || !hd->platform_ops )
|
||||
return 0;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
if ( (rc = hd->platform_ops->assign_device(d, bus, devfn)) )
|
||||
return rc;
|
||||
goto done;
|
||||
|
||||
if ( has_arch_pdevs(d) && !is_hvm_domain(d) && !need_iommu(d) )
|
||||
{
|
||||
d->need_iommu = 1;
|
||||
return iommu_populate_page_table(d);
|
||||
rc = iommu_populate_page_table(d);
|
||||
goto done;
|
||||
}
|
||||
return 0;
|
||||
done:
|
||||
read_unlock(&pcidevs_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int iommu_populate_page_table(struct domain *d)
|
||||
@@ -204,12 +211,29 @@ int iommu_unmap_page(struct domain *d, unsigned long gfn)
|
||||
return hd->platform_ops->unmap_page(d, gfn);
|
||||
}
|
||||
|
||||
void deassign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
int deassign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
{
|
||||
struct hvm_iommu *hd = domain_hvm_iommu(d);
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
if ( !iommu_enabled || !hd->platform_ops )
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
pdev = pci_get_pdev(bus, devfn);
|
||||
if (!pdev)
|
||||
{
|
||||
read_unlock(&pcidevs_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (pdev->domain != d)
|
||||
{
|
||||
read_unlock(&pcidevs_lock);
|
||||
gdprintk(XENLOG_ERR VTDPREFIX,
|
||||
"IOMMU: deassign a device not owned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hd->platform_ops->reassign_device(d, dom0, bus, devfn);
|
||||
|
||||
@@ -218,6 +242,10 @@ void deassign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
d->need_iommu = 0;
|
||||
hd->platform_ops->teardown(d);
|
||||
}
|
||||
|
||||
read_unlock(&pcidevs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iommu_setup(void)
|
||||
|
||||
@@ -41,11 +41,11 @@ struct pci_dev *alloc_pdev(u8 bus, u8 devfn)
|
||||
pdev = xmalloc(struct pci_dev);
|
||||
if ( !pdev )
|
||||
return NULL;
|
||||
memset(pdev, 0, sizeof(struct pci_dev));
|
||||
|
||||
*((u8*) &pdev->bus) = bus;
|
||||
*((u8*) &pdev->devfn) = devfn;
|
||||
pdev->domain = NULL;
|
||||
spin_lock_init(&pdev->lock);
|
||||
INIT_LIST_HEAD(&pdev->msi_list);
|
||||
list_add(&pdev->alldevs_list, &alldevs_list);
|
||||
|
||||
@@ -58,42 +58,35 @@ void free_pdev(struct pci_dev *pdev)
|
||||
xfree(pdev);
|
||||
}
|
||||
|
||||
struct pci_dev *pci_lock_pdev(int bus, int devfn)
|
||||
struct pci_dev *pci_get_pdev(int bus, int devfn)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
|
||||
if ( (pdev->bus == bus || bus == -1) &&
|
||||
(pdev->devfn == devfn || devfn == -1) )
|
||||
{
|
||||
spin_lock(&pdev->lock);
|
||||
read_unlock(&pcidevs_lock);
|
||||
return pdev;
|
||||
}
|
||||
read_unlock(&pcidevs_lock);
|
||||
{
|
||||
return pdev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn)
|
||||
struct pci_dev *pci_get_pdev_by_domain(struct domain *d, int bus, int devfn)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
list_for_each_entry ( pdev, &d->arch.pdev_list, domain_list )
|
||||
{
|
||||
spin_lock(&pdev->lock);
|
||||
if ( (pdev->bus == bus || bus == -1) &&
|
||||
(pdev->devfn == devfn || devfn == -1) &&
|
||||
(pdev->domain == d) )
|
||||
{
|
||||
read_unlock(&pcidevs_lock);
|
||||
return pdev;
|
||||
}
|
||||
spin_unlock(&pdev->lock);
|
||||
}
|
||||
read_unlock(&pcidevs_lock);
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
|
||||
if ( (pdev->bus == bus || bus == -1) &&
|
||||
(pdev->devfn == devfn || devfn == -1) &&
|
||||
(pdev->domain == d) )
|
||||
{
|
||||
return pdev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -109,24 +102,20 @@ int pci_add_device(u8 bus, u8 devfn)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
spin_lock(&pdev->lock);
|
||||
if ( !pdev->domain )
|
||||
{
|
||||
pdev->domain = dom0;
|
||||
ret = iommu_add_device(pdev);
|
||||
if ( ret )
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_add(&pdev->domain_list, &dom0->arch.pdev_list);
|
||||
}
|
||||
spin_unlock(&pdev->lock);
|
||||
printk(XENLOG_DEBUG "PCI add device %02x:%02x.%x\n", bus,
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
|
||||
out:
|
||||
write_unlock(&pcidevs_lock);
|
||||
printk(XENLOG_DEBUG "PCI add device %02x:%02x.%x\n", bus,
|
||||
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -139,7 +128,6 @@ int pci_remove_device(u8 bus, u8 devfn)
|
||||
list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
|
||||
if ( pdev->bus == bus && pdev->devfn == devfn )
|
||||
{
|
||||
spin_lock(&pdev->lock);
|
||||
ret = iommu_remove_device(pdev);
|
||||
if ( pdev->domain )
|
||||
list_del(&pdev->domain_list);
|
||||
@@ -199,14 +187,15 @@ void pci_release_devices(struct domain *d)
|
||||
struct pci_dev *pdev;
|
||||
u8 bus, devfn;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
pci_clean_dpci_irqs(d);
|
||||
while ( (pdev = pci_lock_domain_pdev(d, -1, -1)) )
|
||||
while ( (pdev = pci_get_pdev_by_domain(d, -1, -1)) )
|
||||
{
|
||||
pci_cleanup_msi(pdev);
|
||||
bus = pdev->bus; devfn = pdev->devfn;
|
||||
spin_unlock(&pdev->lock);
|
||||
deassign_device(d, bus, devfn);
|
||||
}
|
||||
read_unlock(&pcidevs_lock);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_MSI_REMAPPING
|
||||
@@ -220,14 +209,12 @@ static void dump_pci_devices(unsigned char ch)
|
||||
|
||||
list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
|
||||
{
|
||||
spin_lock(&pdev->lock);
|
||||
printk("%02x:%02x.%x - dom %-3d - MSIs < ",
|
||||
pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
|
||||
pdev->domain ? pdev->domain->domain_id : -1);
|
||||
list_for_each_entry ( msi, &pdev->msi_list, list )
|
||||
printk("%d ", msi->vector);
|
||||
printk(">\n");
|
||||
spin_unlock(&pdev->lock);
|
||||
}
|
||||
|
||||
read_unlock(&pcidevs_lock);
|
||||
|
||||
@@ -49,15 +49,14 @@ static void setup_dom0_rmrr(struct domain *d);
|
||||
static void context_set_domain_id(struct context_entry *context,
|
||||
struct domain *d)
|
||||
{
|
||||
unsigned long flags;
|
||||
domid_t iommu_domid = domain_iommu_domid(d);
|
||||
|
||||
if ( iommu_domid == 0 )
|
||||
{
|
||||
spin_lock_irqsave(&domid_bitmap_lock, flags);
|
||||
spin_lock(&domid_bitmap_lock);
|
||||
iommu_domid = find_first_zero_bit(domid_bitmap, domid_bitmap_size);
|
||||
set_bit(iommu_domid, domid_bitmap);
|
||||
spin_unlock_irqrestore(&domid_bitmap_lock, flags);
|
||||
spin_unlock(&domid_bitmap_lock);
|
||||
d->arch.hvm_domain.hvm_iommu.iommu_domid = iommu_domid;
|
||||
}
|
||||
|
||||
@@ -140,10 +139,9 @@ int nr_iommus;
|
||||
static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus)
|
||||
{
|
||||
struct root_entry *root, *root_entries;
|
||||
unsigned long flags;
|
||||
u64 maddr;
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
ASSERT(spin_is_locked(&iommu->lock));
|
||||
root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);
|
||||
root = &root_entries[bus];
|
||||
if ( !root_present(*root) )
|
||||
@@ -152,7 +150,6 @@ static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus)
|
||||
if ( maddr == 0 )
|
||||
{
|
||||
unmap_vtd_domain_page(root_entries);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
set_root_value(*root, maddr);
|
||||
@@ -161,36 +158,9 @@ static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus)
|
||||
}
|
||||
maddr = (u64) get_context_addr(*root);
|
||||
unmap_vtd_domain_page(root_entries);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return maddr;
|
||||
}
|
||||
|
||||
static int device_context_mapped(struct iommu *iommu, u8 bus, u8 devfn)
|
||||
{
|
||||
struct root_entry *root, *root_entries;
|
||||
struct context_entry *context;
|
||||
u64 context_maddr;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);
|
||||
root = &root_entries[bus];
|
||||
if ( !root_present(*root) )
|
||||
{
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
context_maddr = get_context_addr(*root);
|
||||
context = (struct context_entry *)map_vtd_domain_page(context_maddr);
|
||||
ret = context_present(context[devfn]);
|
||||
unmap_vtd_domain_page(context);
|
||||
out:
|
||||
unmap_vtd_domain_page(root_entries);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr, int alloc)
|
||||
{
|
||||
struct hvm_iommu *hd = domain_hvm_iommu(domain);
|
||||
@@ -198,12 +168,11 @@ static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr, int alloc)
|
||||
struct dma_pte *parent, *pte = NULL;
|
||||
int level = agaw_to_level(hd->agaw);
|
||||
int offset;
|
||||
unsigned long flags;
|
||||
u64 pte_maddr = 0, maddr;
|
||||
u64 *vaddr = NULL;
|
||||
|
||||
addr &= (((u64)1) << addr_width) - 1;
|
||||
spin_lock_irqsave(&hd->mapping_lock, flags);
|
||||
ASSERT(spin_is_locked(&hd->mapping_lock));
|
||||
if ( hd->pgd_maddr == 0 )
|
||||
if ( !alloc || ((hd->pgd_maddr = alloc_pgtable_maddr(domain)) == 0) )
|
||||
goto out;
|
||||
@@ -252,7 +221,6 @@ static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr, int alloc)
|
||||
|
||||
unmap_vtd_domain_page(parent);
|
||||
out:
|
||||
spin_unlock_irqrestore(&hd->mapping_lock, flags);
|
||||
return pte_maddr;
|
||||
}
|
||||
|
||||
@@ -536,22 +504,30 @@ static void dma_pte_clear_one(struct domain *domain, u64 addr)
|
||||
struct dma_pte *page = NULL, *pte = NULL;
|
||||
u64 pg_maddr;
|
||||
|
||||
spin_lock(&hd->mapping_lock);
|
||||
/* get last level pte */
|
||||
pg_maddr = addr_to_dma_page_maddr(domain, addr, 0);
|
||||
if ( pg_maddr == 0 )
|
||||
{
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
|
||||
pte = page + address_level_offset(addr, 1);
|
||||
|
||||
if ( !dma_pte_present(*pte) )
|
||||
{
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
unmap_vtd_domain_page(page);
|
||||
return;
|
||||
}
|
||||
|
||||
dma_clear_pte(*pte);
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
iommu_flush_cache_entry(pte);
|
||||
|
||||
/* No need pcidevs_lock here since do that on assign/deassign device*/
|
||||
for_each_drhd_unit ( drhd )
|
||||
{
|
||||
iommu = drhd->iommu;
|
||||
@@ -598,16 +574,18 @@ static int iommu_set_root_entry(struct iommu *iommu)
|
||||
unsigned long flags;
|
||||
s_time_t start_time;
|
||||
|
||||
spin_lock_irqsave(&iommu->register_lock, flags);
|
||||
spin_lock(&iommu->lock);
|
||||
|
||||
if ( iommu->root_maddr == 0 )
|
||||
iommu->root_maddr = alloc_pgtable_maddr(NULL);
|
||||
if ( iommu->root_maddr == 0 )
|
||||
{
|
||||
spin_unlock_irqrestore(&iommu->register_lock, flags);
|
||||
spin_unlock(&iommu->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_unlock(&iommu->lock);
|
||||
spin_lock_irqsave(&iommu->register_lock, flags);
|
||||
dmar_writeq(iommu->reg, DMAR_RTADDR_REG, iommu->root_maddr);
|
||||
cmd = iommu->gcmd | DMA_GCMD_SRTP;
|
||||
dmar_writel(iommu->reg, DMAR_GCMD_REG, cmd);
|
||||
@@ -742,9 +720,7 @@ static void iommu_page_fault(int vector, void *dev_id,
|
||||
dprintk(XENLOG_WARNING VTDPREFIX,
|
||||
"iommu_page_fault: iommu->reg = %p\n", iommu->reg);
|
||||
|
||||
spin_lock_irqsave(&iommu->register_lock, flags);
|
||||
fault_status = dmar_readl(iommu->reg, DMAR_FSTS_REG);
|
||||
spin_unlock_irqrestore(&iommu->register_lock, flags);
|
||||
|
||||
iommu_fault_status(fault_status);
|
||||
|
||||
@@ -1057,21 +1033,30 @@ static int domain_context_mapping_one(
|
||||
{
|
||||
struct hvm_iommu *hd = domain_hvm_iommu(domain);
|
||||
struct context_entry *context, *context_entries;
|
||||
unsigned long flags;
|
||||
u64 maddr, pgd_maddr;
|
||||
struct pci_dev *pdev = NULL;
|
||||
int agaw;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
spin_lock(&iommu->lock);
|
||||
maddr = bus_to_context_maddr(iommu, bus);
|
||||
context_entries = (struct context_entry *)map_vtd_domain_page(maddr);
|
||||
context = &context_entries[devfn];
|
||||
|
||||
if ( context_present(*context) )
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pdev = pci_get_pdev(bus, devfn);
|
||||
if (!pdev)
|
||||
res = -ENODEV;
|
||||
else if (pdev->domain != domain)
|
||||
res = -EINVAL;
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
return 0;
|
||||
spin_unlock(&iommu->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
if ( iommu_passthrough &&
|
||||
ecap_pass_thru(iommu->ecap) && (domain->domain_id == 0) )
|
||||
{
|
||||
@@ -1080,6 +1065,8 @@ static int domain_context_mapping_one(
|
||||
}
|
||||
else
|
||||
{
|
||||
spin_lock(&hd->mapping_lock);
|
||||
|
||||
/* Ensure we have pagetables allocated down to leaf PTE. */
|
||||
if ( hd->pgd_maddr == 0 )
|
||||
{
|
||||
@@ -1087,8 +1074,9 @@ static int domain_context_mapping_one(
|
||||
if ( hd->pgd_maddr == 0 )
|
||||
{
|
||||
nomem:
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
spin_unlock(&iommu->lock);
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
@@ -1108,6 +1096,7 @@ static int domain_context_mapping_one(
|
||||
|
||||
context_set_address_root(*context, pgd_maddr);
|
||||
context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL);
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1119,8 +1108,7 @@ static int domain_context_mapping_one(
|
||||
context_set_fault_enable(*context);
|
||||
context_set_present(*context);
|
||||
iommu_flush_cache_entry(context);
|
||||
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
/* Context entry was previously non-present (with domid 0). */
|
||||
if ( iommu_flush_context_device(iommu, 0, (((u16)bus) << 8) | devfn,
|
||||
@@ -1130,7 +1118,8 @@ static int domain_context_mapping_one(
|
||||
iommu_flush_iotlb_dsi(iommu, 0, 1);
|
||||
|
||||
set_bit(iommu->index, &hd->iommu_bitmap);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1174,17 +1163,15 @@ int pdev_type(u8 bus, u8 devfn)
|
||||
}
|
||||
|
||||
#define MAX_BUSES 256
|
||||
static DEFINE_SPINLOCK(bus2bridge_lock);
|
||||
static struct { u8 map, bus, devfn; } bus2bridge[MAX_BUSES];
|
||||
|
||||
static int find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
|
||||
static int _find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
|
||||
{
|
||||
int cnt = 0;
|
||||
*secbus = *bus;
|
||||
|
||||
if ( *bus == 0 )
|
||||
/* assume integrated PCI devices in RC have valid requester-id */
|
||||
return 1;
|
||||
|
||||
ASSERT(spin_is_locked(&bus2bridge_lock));
|
||||
if ( !bus2bridge[*bus].map )
|
||||
return 0;
|
||||
|
||||
@@ -1200,6 +1187,21 @@ static int find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ( *bus == 0 )
|
||||
/* assume integrated PCI devices in RC have valid requester-id */
|
||||
return 1;
|
||||
|
||||
spin_lock(&bus2bridge_lock);
|
||||
ret = _find_pcie_endpoint(bus, devfn, secbus);
|
||||
spin_unlock(&bus2bridge_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
|
||||
{
|
||||
struct acpi_drhd_unit *drhd;
|
||||
@@ -1212,6 +1214,8 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
|
||||
if ( !drhd )
|
||||
return -ENODEV;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
type = pdev_type(bus, devfn);
|
||||
switch ( type )
|
||||
{
|
||||
@@ -1226,12 +1230,14 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
|
||||
if ( type == DEV_TYPE_PCIe_BRIDGE )
|
||||
break;
|
||||
|
||||
spin_lock(&bus2bridge_lock);
|
||||
for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
|
||||
{
|
||||
bus2bridge[sec_bus].map = 1;
|
||||
bus2bridge[sec_bus].bus = bus;
|
||||
bus2bridge[sec_bus].devfn = devfn;
|
||||
}
|
||||
spin_unlock(&bus2bridge_lock);
|
||||
break;
|
||||
|
||||
case DEV_TYPE_PCIe_ENDPOINT:
|
||||
@@ -1290,20 +1296,22 @@ static int domain_context_unmap_one(
|
||||
u8 bus, u8 devfn)
|
||||
{
|
||||
struct context_entry *context, *context_entries;
|
||||
unsigned long flags;
|
||||
u64 maddr;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
spin_lock(&iommu->lock);
|
||||
|
||||
maddr = bus_to_context_maddr(iommu, bus);
|
||||
context_entries = (struct context_entry *)map_vtd_domain_page(maddr);
|
||||
context = &context_entries[devfn];
|
||||
|
||||
if ( !context_present(*context) )
|
||||
{
|
||||
spin_unlock(&iommu->lock);
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
context_clear_present(*context);
|
||||
context_clear_entry(*context);
|
||||
iommu_flush_cache_entry(context);
|
||||
@@ -1315,8 +1323,8 @@ static int domain_context_unmap_one(
|
||||
else
|
||||
iommu_flush_iotlb_dsi(iommu, domain_iommu_domid(domain), 0);
|
||||
|
||||
spin_unlock(&iommu->lock);
|
||||
unmap_vtd_domain_page(context_entries);
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1380,7 +1388,10 @@ static int reassign_device_ownership(
|
||||
struct iommu *pdev_iommu;
|
||||
int ret, found = 0;
|
||||
|
||||
if ( !(pdev = pci_lock_domain_pdev(source, bus, devfn)) )
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
pdev = pci_get_pdev_by_domain(source, bus, devfn);
|
||||
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
drhd = acpi_find_matched_drhd_unit(bus, devfn);
|
||||
@@ -1391,14 +1402,9 @@ static int reassign_device_ownership(
|
||||
if ( ret )
|
||||
return ret;
|
||||
|
||||
write_lock(&pcidevs_lock);
|
||||
list_move(&pdev->domain_list, &target->arch.pdev_list);
|
||||
write_unlock(&pcidevs_lock);
|
||||
pdev->domain = target;
|
||||
|
||||
spin_unlock(&pdev->lock);
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
for_each_pdev ( source, pdev )
|
||||
{
|
||||
drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn);
|
||||
@@ -1408,7 +1414,6 @@ static int reassign_device_ownership(
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&pcidevs_lock);
|
||||
|
||||
if ( !found )
|
||||
clear_bit(pdev_iommu->index, &source_hd->iommu_bitmap);
|
||||
@@ -1423,22 +1428,15 @@ void iommu_domain_teardown(struct domain *d)
|
||||
if ( list_empty(&acpi_drhd_units) )
|
||||
return;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
spin_lock(&hd->mapping_lock);
|
||||
iommu_free_pagetable(hd->pgd_maddr, agaw_to_level(hd->agaw));
|
||||
hd->pgd_maddr = 0;
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
|
||||
iommu_domid_release(d);
|
||||
}
|
||||
|
||||
static int domain_context_mapped(u8 bus, u8 devfn)
|
||||
{
|
||||
struct acpi_drhd_unit *drhd;
|
||||
|
||||
for_each_drhd_unit ( drhd )
|
||||
if ( device_context_mapped(drhd->iommu, bus, devfn) )
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_iommu_map_page(
|
||||
struct domain *d, unsigned long gfn, unsigned long mfn)
|
||||
{
|
||||
@@ -1457,17 +1455,27 @@ int intel_iommu_map_page(
|
||||
ecap_pass_thru(iommu->ecap) && (d->domain_id == 0) )
|
||||
return 0;
|
||||
|
||||
spin_lock(&hd->mapping_lock);
|
||||
|
||||
pg_maddr = addr_to_dma_page_maddr(d, (paddr_t)gfn << PAGE_SHIFT_4K, 1);
|
||||
if ( pg_maddr == 0 )
|
||||
{
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
|
||||
pte = page + (gfn & LEVEL_MASK);
|
||||
pte_present = dma_pte_present(*pte);
|
||||
dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K);
|
||||
dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE);
|
||||
iommu_flush_cache_entry(pte);
|
||||
spin_unlock(&hd->mapping_lock);
|
||||
unmap_vtd_domain_page(page);
|
||||
|
||||
/*
|
||||
* No need pcideves_lock here because we have flush
|
||||
* when assign/deassign device
|
||||
*/
|
||||
for_each_drhd_unit ( drhd )
|
||||
{
|
||||
iommu = drhd->iommu;
|
||||
@@ -1510,6 +1518,7 @@ static int iommu_prepare_rmrr_dev(struct domain *d,
|
||||
u64 base, end;
|
||||
unsigned long base_pfn, end_pfn;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
ASSERT(rmrr->base_address < rmrr->end_address);
|
||||
|
||||
base = rmrr->base_address & PAGE_MASK_4K;
|
||||
@@ -1523,8 +1532,7 @@ static int iommu_prepare_rmrr_dev(struct domain *d,
|
||||
base_pfn++;
|
||||
}
|
||||
|
||||
if ( domain_context_mapped(bus, devfn) == 0 )
|
||||
ret = domain_context_mapping(d, bus, devfn);
|
||||
ret = domain_context_mapping(d, bus, devfn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1535,6 +1543,8 @@ static int intel_iommu_add_device(struct pci_dev *pdev)
|
||||
u16 bdf;
|
||||
int ret, i;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
|
||||
if ( !pdev->domain )
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1689,6 +1699,7 @@ static void setup_dom0_rmrr(struct domain *d)
|
||||
u16 bdf;
|
||||
int ret, i;
|
||||
|
||||
read_lock(&pcidevs_lock);
|
||||
for_each_rmrr_device ( rmrr, bdf, i )
|
||||
{
|
||||
ret = iommu_prepare_rmrr_dev(d, rmrr, PCI_BUS(bdf), PCI_DEVFN2(bdf));
|
||||
@@ -1696,6 +1707,7 @@ static void setup_dom0_rmrr(struct domain *d)
|
||||
gdprintk(XENLOG_ERR VTDPREFIX,
|
||||
"IOMMU: mapping reserved region failed\n");
|
||||
}
|
||||
read_unlock(&pcidevs_lock);
|
||||
}
|
||||
|
||||
int intel_vtd_setup(void)
|
||||
@@ -1748,27 +1760,43 @@ int device_assigned(u8 bus, u8 devfn)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if ( (pdev = pci_lock_domain_pdev(dom0, bus, devfn)) )
|
||||
read_lock(&pcidevs_lock);
|
||||
pdev = pci_get_pdev_by_domain(dom0, bus, devfn);
|
||||
if (!pdev)
|
||||
{
|
||||
spin_unlock(&pdev->lock);
|
||||
return 0;
|
||||
read_unlock(&pcidevs_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
read_unlock(&pcidevs_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
{
|
||||
struct acpi_rmrr_unit *rmrr;
|
||||
int ret = 0, i;
|
||||
struct pci_dev *pdev;
|
||||
u16 bdf;
|
||||
|
||||
if ( list_empty(&acpi_drhd_units) )
|
||||
return -ENODEV;
|
||||
|
||||
ASSERT(spin_is_locked(&pcidevs_lock));
|
||||
pdev = pci_get_pdev(bus, devfn);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
if (pdev->domain != dom0)
|
||||
{
|
||||
gdprintk(XENLOG_ERR VTDPREFIX,
|
||||
"IOMMU: assign a assigned device\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = reassign_device_ownership(dom0, d, bus, devfn);
|
||||
if ( ret )
|
||||
return ret;
|
||||
goto done;
|
||||
|
||||
/* Setup rmrr identity mapping */
|
||||
for_each_rmrr_device( rmrr, bdf, i )
|
||||
@@ -1779,16 +1807,20 @@ int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
* ignore USB RMRR temporarily.
|
||||
*/
|
||||
if ( is_usb_device(bus, devfn) )
|
||||
return 0;
|
||||
{
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = iommu_prepare_rmrr_dev(d, rmrr, bus, devfn);
|
||||
if ( ret )
|
||||
gdprintk(XENLOG_ERR VTDPREFIX,
|
||||
"IOMMU: mapping reserved region failed\n");
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,13 +68,17 @@ struct msi_msg {
|
||||
u32 data; /* 16 bits of msi message data */
|
||||
};
|
||||
|
||||
struct msi_desc;
|
||||
/* Helper functions */
|
||||
extern void mask_msi_vector(unsigned int vector);
|
||||
extern void unmask_msi_vector(unsigned int vector);
|
||||
extern void set_msi_affinity(unsigned int vector, cpumask_t mask);
|
||||
extern int pci_enable_msi(struct msi_info *msi);
|
||||
extern void pci_disable_msi(int vector);
|
||||
extern int pci_enable_msi(struct msi_info *msi, struct msi_desc **desc);
|
||||
extern void pci_disable_msi(struct msi_desc *desc);
|
||||
extern void pci_cleanup_msi(struct pci_dev *pdev);
|
||||
extern int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
|
||||
extern void teardown_msi_vector(int vector);
|
||||
extern int msi_free_vector(struct msi_desc *entry);
|
||||
|
||||
struct msi_desc {
|
||||
struct {
|
||||
|
||||
@@ -62,7 +62,7 @@ int iommu_domain_init(struct domain *d);
|
||||
void iommu_domain_destroy(struct domain *d);
|
||||
int device_assigned(u8 bus, u8 devfn);
|
||||
int assign_device(struct domain *d, u8 bus, u8 devfn);
|
||||
void deassign_device(struct domain *d, u8 bus, u8 devfn);
|
||||
int deassign_device(struct domain *d, u8 bus, u8 devfn);
|
||||
int iommu_get_device_group(struct domain *d, u8 bus, u8 devfn,
|
||||
XEN_GUEST_HANDLE_64(uint32) buf, int max_sdevs);
|
||||
int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn);
|
||||
|
||||
@@ -36,7 +36,6 @@ struct pci_dev {
|
||||
struct domain *domain;
|
||||
const u8 bus;
|
||||
const u8 devfn;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
#define for_each_pdev(domain, pdev) \
|
||||
@@ -59,6 +58,8 @@ struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn);
|
||||
void pci_release_devices(struct domain *d);
|
||||
int pci_add_device(u8 bus, u8 devfn);
|
||||
int pci_remove_device(u8 bus, u8 devfn);
|
||||
struct pci_dev *pci_get_pdev(int bus, int devfn);
|
||||
struct pci_dev *pci_get_pdev_by_domain(struct domain *d, int bus, int devfn);
|
||||
|
||||
uint8_t pci_conf_read8(
|
||||
unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg);
|
||||
|
||||