VT-d: pci code cleanup

This patch moves the pci code from iommu.c to pci.c. Instead of setup
pci hierarchy in array bus2bridge in iommu_context_mapping, use
scan_pci_devices once to add all existed PCI devices in system to
alldevs_list and setup pci hierarchy in array bus2bridge. In addition,
implement find_upstream_bridge to find the upstream PCIe-to-PCI/PCIX
bridge or PCI legacy bridge for a PCI device, therefore it's cleanly
to handle context map/unmap for PCI device, even for source-id
setting.

Signed-off-by: Weidong Han <weidong.han@intel.com>
This commit is contained in:
Keir Fraser committed 2009-06-19 08:45:20 +01:00
1 parent faefc018df
commit 575dd4a3ef
3 files changed
+208 -145

No files matched your search

+153
View File
@@ -30,6 +30,16 @@
LIST_HEAD(alldevs_list);
spinlock_t pcidevs_lock = SPIN_LOCK_UNLOCKED;
#define MAX_BUSES 256
static struct {
u8 map;
u8 bus;
u8 devfn;
} bus2bridge[MAX_BUSES];
/* bus2bridge_lock protects bus2bridge array */
static DEFINE_SPINLOCK(bus2bridge_lock);
struct pci_dev *alloc_pdev(u8 bus, u8 devfn)
{
struct pci_dev *pdev;
@@ -240,6 +250,149 @@ void pci_release_devices(struct domain *d)
spin_unlock(&pcidevs_lock);
}
#define PCI_CLASS_BRIDGE_PCI 0x0604
int pdev_type(u8 bus, u8 devfn)
{
u16 class_device;
u16 status, creg;
int pos;
u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
if ( class_device == PCI_CLASS_BRIDGE_PCI )
{
pos = pci_find_next_cap(bus, devfn,
PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
if ( !pos )
return DEV_TYPE_LEGACY_PCI_BRIDGE;
creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
DEV_TYPE_PCIe2PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
}
status = pci_conf_read16(bus, d, f, PCI_STATUS);
if ( !(status & PCI_STATUS_CAP_LIST) )
return DEV_TYPE_PCI;
if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
return DEV_TYPE_PCIe_ENDPOINT;
return DEV_TYPE_PCI;
}
/*
* find the upstream PCIe-to-PCI/PCIX bridge or PCI legacy bridge
* return 0: the device is integrated PCI device or PCIe
* return 1: find PCIe-to-PCI/PCIX bridge or PCI legacy bridge
* return -1: fail
*/
int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus)
{
int ret = 0;
int cnt = 0;
if ( *bus == 0 )
return 0;
if ( !bus2bridge[*bus].map )
return 0;
ret = 1;
spin_lock(&bus2bridge_lock);
while ( bus2bridge[*bus].map )
{
*secbus = *bus;
*devfn = bus2bridge[*bus].devfn;
*bus = bus2bridge[*bus].bus;
if ( cnt++ >= MAX_BUSES )
{
ret = -1;
goto out;
}
}
out:
spin_unlock(&bus2bridge_lock);
return ret;
}
/*
* scan pci devices to add all existed PCI devices to alldevs_list,
* and setup pci hierarchy in array bus2bridge. This function is only
* called in VT-d hardware setup
*/
int __init scan_pci_devices(void)
{
struct pci_dev *pdev;
int bus, dev, func;
u8 sec_bus, sub_bus;
int type;
u32 l;
spin_lock(&pcidevs_lock);
for ( bus = 0; bus < 256; bus++ )
{
for ( dev = 0; dev < 32; dev++ )
{
for ( func = 0; func < 8; func++ )
{
l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
/* some broken boards return 0 or ~0 if a slot is empty: */
if ( (l == 0xffffffff) || (l == 0x00000000) ||
(l == 0x0000ffff) || (l == 0xffff0000) )
continue;
pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
if ( !pdev )
{
printk("%s: alloc_pdev failed.\n", __func__);
spin_unlock(&pcidevs_lock);
return -ENOMEM;
}
/* build bus2bridge */
type = pdev_type(bus, PCI_DEVFN(dev, func));
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
break;
case DEV_TYPE_PCIe2PCI_BRIDGE:
case DEV_TYPE_LEGACY_PCI_BRIDGE:
sec_bus = pci_conf_read8(bus, dev, func,
PCI_SECONDARY_BUS);
sub_bus = pci_conf_read8(bus, dev, func,
PCI_SUBORDINATE_BUS);
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 = PCI_DEVFN(dev, func);
}
spin_unlock(&bus2bridge_lock);
break;
case DEV_TYPE_PCIe_ENDPOINT:
case DEV_TYPE_PCI:
break;
default:
printk("%s: unknown type: bdf = %x:%x.%x\n",
__func__, bus, dev, func);
spin_unlock(&pcidevs_lock);
return -EINVAL;
}
}
}
}
spin_unlock(&pcidevs_lock);
return 0;
}
#ifdef SUPPORT_MSI_REMAPPING
static void dump_pci_devices(unsigned char ch)
{
+44 -145
View File
@@ -1066,92 +1066,12 @@ static int domain_context_mapping_one(
return 0;
}
#define PCI_BASE_CLASS_BRIDGE 0x06
#define PCI_CLASS_BRIDGE_PCI 0x0604
enum {
DEV_TYPE_PCIe_ENDPOINT,
DEV_TYPE_PCIe_BRIDGE, // PCIe root port, switch
DEV_TYPE_PCI_BRIDGE, // PCIe-to-PCI/PCIx bridge, PCI-to-PCI bridge
DEV_TYPE_PCI,
};
int pdev_type(u8 bus, u8 devfn)
{
u16 class_device;
u16 status, creg;
int pos;
u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
if ( class_device == PCI_CLASS_BRIDGE_PCI )
{
pos = pci_find_next_cap(bus, devfn,
PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
if ( !pos )
return DEV_TYPE_PCI_BRIDGE;
creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
DEV_TYPE_PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
}
status = pci_conf_read16(bus, d, f, PCI_STATUS);
if ( !(status & PCI_STATUS_CAP_LIST) )
return DEV_TYPE_PCI;
if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
return DEV_TYPE_PCIe_ENDPOINT;
return DEV_TYPE_PCI;
}
#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)
{
int cnt = 0;
*secbus = *bus;
ASSERT(spin_is_locked(&bus2bridge_lock));
if ( !bus2bridge[*bus].map )
return 0;
while ( bus2bridge[*bus].map )
{
*secbus = *bus;
*devfn = bus2bridge[*bus].devfn;
*bus = bus2bridge[*bus].bus;
if ( cnt++ >= MAX_BUSES )
return 0;
}
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;
int ret = 0;
u16 sec_bus, sub_bus;
u32 type;
u8 secbus, secdevfn;
u8 secbus;
struct pci_dev *pdev = pci_get_pdev(bus, devfn);
if ( pdev == NULL )
@@ -1179,22 +1099,8 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
break;
case DEV_TYPE_PCI_BRIDGE:
sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
PCI_SECONDARY_BUS);
sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
PCI_SUBORDINATE_BUS);
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);
case DEV_TYPE_PCIe2PCI_BRIDGE:
case DEV_TYPE_LEGACY_PCI_BRIDGE:
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -1211,31 +1117,29 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
if ( ret )
break;
break;
secbus = bus;
secdevfn = devfn;
/* dependent devices mapping */
while ( bus2bridge[bus].map )
if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
break;
/* PCIe to PCI/PCIx bridge */
if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
{
secbus = bus;
secdevfn = devfn;
devfn = bus2bridge[bus].devfn;
bus = bus2bridge[bus].bus;
ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
if ( ret )
return ret;
}
if ( (secbus != bus) && (secdevfn != 0) )
/*
* The source-id for transactions on non-PCIe buses seem
* to originate from devfn=0 on the secondary bus behind
* the bridge. Map that id as well. The id to use in
* these scanarios is not particularly well documented
* anywhere.
* Devices behind PCIe-to-PCI/PCIx bridge may generate
* different requester-id. It may originate from devfn=0
* on the secondary bus behind the bridge. Map that id
* as well.
*/
ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0);
}
else /* Legacy PCI bridge */
ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
break;
default:
@@ -1296,7 +1200,7 @@ static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn)
struct acpi_drhd_unit *drhd;
int ret = 0;
u32 type;
u8 secbus, secdevfn;
u8 secbus;
struct pci_dev *pdev = pci_get_pdev(bus, devfn);
BUG_ON(!pdev);
@@ -1309,7 +1213,8 @@ static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn)
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
case DEV_TYPE_PCI_BRIDGE:
case DEV_TYPE_PCIe2PCI_BRIDGE:
case DEV_TYPE_LEGACY_PCI_BRIDGE:
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -1327,22 +1232,21 @@ static int domain_context_unmap(struct domain *domain, u8 bus, u8 devfn)
if ( ret )
break;
secbus = bus;
secdevfn = devfn;
/* dependent devices unmapping */
while ( bus2bridge[bus].map )
if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
break;
/* PCIe to PCI/PCIx bridge */
if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
{
secbus = bus;
secdevfn = devfn;
devfn = bus2bridge[bus].devfn;
bus = bus2bridge[bus].bus;
ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
if ( ret )
return ret;
}
if ( (secbus != bus) && (secdevfn != 0) )
ret = domain_context_unmap_one(domain, drhd->iommu, secbus, 0);
}
else /* Legacy PCI bridge */
ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
break;
default:
@@ -1584,31 +1488,24 @@ static void setup_dom0_devices(struct domain *d)
{
struct hvm_iommu *hd;
struct pci_dev *pdev;
int bus, dev, func;
u32 l;
int bus, devfn;
hd = domain_hvm_iommu(d);
spin_lock(&pcidevs_lock);
for ( bus = 0; bus < 256; bus++ )
{
for ( dev = 0; dev < 32; dev++ )
for ( devfn = 0; devfn < 256; devfn++ )
{
for ( func = 0; func < 8; func++ )
{
l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
/* some broken boards return 0 or ~0 if a slot is empty: */
if ( (l == 0xffffffff) || (l == 0x00000000) ||
(l == 0x0000ffff) || (l == 0xffff0000) )
continue;
pdev = pci_get_pdev(bus, devfn);
if ( !pdev )
continue;
pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
pdev->domain = d;
list_add(&pdev->domain_list, &d->arch.pdev_list);
domain_context_mapping(d, pdev->bus, pdev->devfn);
if ( ats_device(0, pdev->bus, pdev->devfn) )
enable_ats_device(0, pdev->bus, pdev->devfn);
}
pdev->domain = d;
list_add(&pdev->domain_list, &d->arch.pdev_list);
domain_context_mapping(d, pdev->bus, pdev->devfn);
if ( ats_device(0, pdev->bus, pdev->devfn) )
enable_ats_device(0, pdev->bus, pdev->devfn);
}
}
spin_unlock(&pcidevs_lock);
@@ -1809,6 +1706,8 @@ int intel_vtd_setup(void)
memset(domid_bitmap, 0, domid_bitmap_size / 8);
set_bit(0, domid_bitmap);
scan_pci_devices();
if ( init_vtd_hw() )
goto error;
@@ -1902,10 +1801,10 @@ done:
static int intel_iommu_group_id(u8 bus, u8 devfn)
{
u8 secbus;
if ( !bus2bridge[bus].map || find_pcie_endpoint(&bus, &devfn, &secbus) )
return PCI_BDF2(bus, devfn);
else
if ( find_upstream_bridge(&bus, &devfn, &secbus) < 0 )
return -1;
else
return PCI_BDF2(bus, devfn);
}
static u32 iommu_state[MAX_IOMMUS][MAX_IOMMU_REGS];
+11
View File
@@ -66,6 +66,17 @@ struct pci_dev {
extern spinlock_t pcidevs_lock;
enum {
DEV_TYPE_PCIe_ENDPOINT,
DEV_TYPE_PCIe_BRIDGE, // PCIe root port, switch
DEV_TYPE_PCIe2PCI_BRIDGE, // PCIe-to-PCI/PCIx bridge
DEV_TYPE_LEGACY_PCI_BRIDGE, // Legacy PCI bridge
DEV_TYPE_PCI,
};
int scan_pci_devices(void);
int pdev_type(u8 bus, u8 devfn);
int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus);
struct pci_dev *alloc_pdev(u8 bus, u8 devfn);
void free_pdev(struct pci_dev *pdev);
struct pci_dev *pci_lock_pdev(int bus, int devfn);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.