diff options
author | Kyösti Mälkki <kyosti.malkki@gmail.com> | 2019-01-09 11:31:25 +0200 |
---|---|---|
committer | Kyösti Mälkki <kyosti.malkki@gmail.com> | 2019-01-10 12:47:18 +0000 |
commit | 8712aa107f4a4a0f64684e00c03eb2ec0d8baf6b (patch) | |
tree | 8960bb511bcefe4031560e9eeba1b8c78d285439 /src/device/pci_device.c | |
parent | 82a4e27341bf292925d25779985a38f70a9858aa (diff) |
device/pci_device: Do not break tree topology
Fix regression introduced with commit
ad7674e device: Introduce pcidev_on_root() and friends
Function pci_scan_bus() breaks bus->children link
in the devicetree topology while scanning a bus.
While the scan is in progress, accessing PCI
devices with number higher than what is being probed
was not possible because new pcidev_on_root() relies
on having proper topology present at any time.
Change-Id: I7bb497f7390628dd2f0310b380f199783a888c4c
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: https://review.coreboot.org/c/30772
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Tristan Corrick <tristan@corrick.kiwi>
Diffstat (limited to 'src/device/pci_device.c')
-rw-r--r-- | src/device/pci_device.c | 79 |
1 files changed, 43 insertions, 36 deletions
diff --git a/src/device/pci_device.c b/src/device/pci_device.c index e35c22c113..82033a6ad9 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -989,33 +989,33 @@ bad: /** * See if we have already allocated a device structure for a given devfn. * - * Given a linked list of PCI device structures and a devfn number, find the - * device structure correspond to the devfn, if present. This function also - * removes the device structure from the linked list. + * Given a PCI bus structure and a devfn number, find the device structure + * corresponding to the devfn, if present. Then move the device structure + * as the last child on the bus. * - * @param list The device structure list. + * @param bus Pointer to the bus structure. * @param devfn A device/function number. * @return Pointer to the device structure found or NULL if we have not * allocated a device for this devfn yet. */ -static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) +static struct device *pci_scan_get_dev(struct bus *bus, unsigned int devfn) { - struct device *dev; - - dev = 0; - for (; *list; list = &(*list)->sibling) { - if ((*list)->path.type != DEVICE_PATH_PCI) { + struct device *dev, **prev; + + prev = &bus->children; + for (dev = bus->children; dev; dev = dev->sibling) { + if (dev->path.type == DEVICE_PATH_PCI) { + if (dev->path.pci.devfn == devfn) { + /* Unlink from the list. */ + *prev = dev->sibling; + dev->sibling = NULL; + break; + } + } else { printk(BIOS_ERR, "child %s not a PCI device\n", - dev_path(*list)); - continue; - } - if ((*list)->path.pci.devfn == devfn) { - /* Unlink from the list. */ - dev = *list; - *list = (*list)->sibling; - dev->sibling = NULL; - break; + dev_path(dev)); } + prev = &dev->sibling; } /* @@ -1027,15 +1027,15 @@ static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) if (dev) { struct device *child; - /* Find the last child of our parent. */ - for (child = dev->bus->children; child && child->sibling;) + /* Find the last child on the bus. */ + for (child = bus->children; child && child->sibling;) child = child->sibling; - /* Place the device on the list of children of its parent. */ + /* Place the device as last on the bus. */ if (child) child->sibling = dev; else - dev->bus->children = dev; + bus->children = dev; } return dev; @@ -1180,7 +1180,8 @@ void pci_scan_bus(struct bus *bus, unsigned min_devfn, unsigned max_devfn) { unsigned int devfn; - struct device *old_devices; + struct device *dev, **prev; + int once = 0; printk(BIOS_DEBUG, "PCI: pci_scan_bus for bus %02x\n", bus->secondary); @@ -1193,9 +1194,6 @@ void pci_scan_bus(struct bus *bus, unsigned min_devfn, max_devfn=0xff; } - old_devices = bus->children; - bus->children = NULL; - post_code(0x24); /* @@ -1203,10 +1201,8 @@ void pci_scan_bus(struct bus *bus, unsigned min_devfn, * non-existence and single function devices. */ for (devfn = min_devfn; devfn <= max_devfn; devfn++) { - struct device *dev; - /* First thing setup the device structure. */ - dev = pci_scan_get_dev(&old_devices, devfn); + dev = pci_scan_get_dev(bus, devfn); /* See if a device is present and setup the device structure. */ dev = pci_probe_dev(dev, bus, devfn); @@ -1228,15 +1224,26 @@ void pci_scan_bus(struct bus *bus, unsigned min_devfn, * Warn if any leftover static devices are are found. * There's probably a problem in devicetree.cb. */ - if (old_devices) { - struct device *left; - printk(BIOS_WARNING, "PCI: Left over static devices:\n"); - for (left = old_devices; left; left = left->sibling) - printk(BIOS_WARNING, "%s\n", dev_path(left)); - printk(BIOS_WARNING, "PCI: Check your devicetree.cb.\n"); + prev = &bus->children; + for (dev = bus->children; dev; dev = dev->sibling) { + /* If we read valid vendor id, it is not leftover device. */ + if (dev->vendor != 0) { + prev = &dev->sibling; + continue; + } + + /* Unlink it from list. */ + *prev = dev->sibling; + + if (!once++) + printk(BIOS_WARNING, "PCI: Leftover static devices:\n"); + printk(BIOS_WARNING, "%s\n", dev_path(dev)); } + if (once) + printk(BIOS_WARNING, "PCI: Check your devicetree.cb.\n"); + /* * For all children that implement scan_bus() (i.e. bridges) * scan the bus behind that child. |