Merge tag 'pci-v5.14-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[sfrench/cifs-2.6.git] / drivers / pci / probe.c
index 275204646c68c08b27ef7cdb9596cc638940836e..cd6fcd84885358c2550ce301f790ffe2e29ce6ec 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/hypervisor.h>
 #include <linux/irqdomain.h>
 #include <linux/pm_runtime.h>
+#include <linux/list_sort.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
@@ -874,14 +875,31 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
        dev_set_msi_domain(&bus->dev, d);
 }
 
+static int res_cmp(void *priv, const struct list_head *a,
+                  const struct list_head *b)
+{
+       struct resource_entry *entry1, *entry2;
+
+       entry1 = container_of(a, struct resource_entry, node);
+       entry2 = container_of(b, struct resource_entry, node);
+
+       if (entry1->res->flags != entry2->res->flags)
+               return entry1->res->flags > entry2->res->flags;
+
+       if (entry1->offset != entry2->offset)
+               return entry1->offset > entry2->offset;
+
+       return entry1->res->start > entry2->res->start;
+}
+
 static int pci_register_host_bridge(struct pci_host_bridge *bridge)
 {
        struct device *parent = bridge->dev.parent;
-       struct resource_entry *window, *n;
+       struct resource_entry *window, *next, *n;
        struct pci_bus *bus, *b;
-       resource_size_t offset;
+       resource_size_t offset, next_offset;
        LIST_HEAD(resources);
-       struct resource *res;
+       struct resource *res, *next_res;
        char addr[64], *fmt;
        const char *name;
        int err;
@@ -961,11 +979,35 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
        if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE)
                dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n");
 
+       /* Sort and coalesce contiguous windows */
+       list_sort(NULL, &resources, res_cmp);
+       resource_list_for_each_entry_safe(window, n, &resources) {
+               if (list_is_last(&window->node, &resources))
+                       break;
+
+               next = list_next_entry(window, node);
+               offset = window->offset;
+               res = window->res;
+               next_offset = next->offset;
+               next_res = next->res;
+
+               if (res->flags != next_res->flags || offset != next_offset)
+                       continue;
+
+               if (res->end + 1 == next_res->start) {
+                       next_res->start = res->start;
+                       res->flags = res->start = res->end = 0;
+               }
+       }
+
        /* Add initial resources to the bus */
        resource_list_for_each_entry_safe(window, n, &resources) {
-               list_move_tail(&window->node, &bridge->windows);
                offset = window->offset;
                res = window->res;
+               if (!res->end)
+                       continue;
+
+               list_move_tail(&window->node, &bridge->windows);
 
                if (res->flags & IORESOURCE_BUS)
                        pci_bus_insert_busn_res(bus, bus->number, res->end);
@@ -1576,6 +1618,26 @@ static void set_pcie_untrusted(struct pci_dev *dev)
                dev->untrusted = true;
 }
 
+static void pci_set_removable(struct pci_dev *dev)
+{
+       struct pci_dev *parent = pci_upstream_bridge(dev);
+
+       /*
+        * We (only) consider everything downstream from an external_facing
+        * device to be removable by the user. We're mainly concerned with
+        * consumer platforms with user accessible thunderbolt ports that are
+        * vulnerable to DMA attacks, and we expect those ports to be marked by
+        * the firmware as external_facing. Devices in traditional hotplug
+        * slots can technically be removed, but the expectation is that unless
+        * the port is marked with external_facing, such devices are less
+        * accessible to user / may not be removed by end user, and thus not
+        * exposed as "removable" to userspace.
+        */
+       if (parent &&
+           (parent->external_facing || dev_is_removable(&parent->dev)))
+               dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
+}
+
 /**
  * pci_ext_cfg_is_aliased - Is ext config space just an alias of std config?
  * @dev: PCI device
@@ -1823,6 +1885,8 @@ int pci_setup_device(struct pci_dev *dev)
        /* Early fixups, before probing the BARs */
        pci_fixup_device(pci_fixup_early, dev);
 
+       pci_set_removable(dev);
+
        pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
                 dev->vendor, dev->device, dev->hdr_type, dev->class);
 
@@ -2227,6 +2291,7 @@ static void pci_release_dev(struct device *dev)
        pci_bus_put(pci_dev->bus);
        kfree(pci_dev->driver_override);
        bitmap_free(pci_dev->dma_alias_mask);
+       dev_dbg(dev, "device released\n");
        kfree(pci_dev);
 }