diff mbox series

[v7,18/26] PCI: Treat VGA BARs as immovable

Message ID 20200129152937.311162-19-s.miroshnichenko@yadro.com
State New
Headers show
Series PCI: Allow BAR movement during boot and hotplug | expand

Commit Message

Sergei Miroshnichenko Jan. 29, 2020, 3:29 p.m. UTC
Some framebuffer drivers (efifb) don't act as a PCI driver (like nouveau),
but take information about BARs indirectly - from BIOS for example. This
makes them vulnerable to BAR movement during boot, when setting reported by
BIOS/bootloader differs from new addresses assigned by the kernel.

Until every such driver is aware of movable BARs, mark every VGA BAR as
immovable. Perhaps this is also useful for splash screens, so they don't
flicker.

This makes some BARs and bridge windows immovable during boot, so update
the parent's struct pci_bus->immovable_range when encountered.

Signed-off-by: Sergei Miroshnichenko <s.miroshnichenko@yadro.com>
---
 drivers/pci/probe.c | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index f8f643dac6d1..b810d28ebf96 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -164,6 +164,20 @@  static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
 
 #define PCI_COMMAND_DECODE_ENABLE	(PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
 
+static void expand_immovable_range(struct pci_bus *bus, struct resource *res, int idx)
+{
+	struct resource *immovable_range = &bus->immovable_range[idx];
+
+	if (!immovable_range->start || immovable_range->start > res->start)
+		immovable_range->start = res->start;
+
+	if (immovable_range->end < res->end)
+		immovable_range->end = res->end;
+
+	if (bus->parent)
+		expand_immovable_range(bus->parent, immovable_range, idx);
+}
+
 /**
  * pci_read_base - Read a PCI BAR
  * @dev: the PCI device
@@ -307,11 +321,16 @@  int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 	}
 
 	if (pci_can_move_bars && res->start && !(res->flags & IORESOURCE_IO)) {
-		pci_warn(dev, "ignore the current offset of BAR %llx-%llx\n",
-			 l64, l64 + sz64 - 1);
-		res->start = 0;
-		res->end = sz64 - 1;
-		res->flags |= IORESOURCE_SIZEALIGN;
+		if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
+			expand_immovable_range(dev->bus, res,
+					       pci_get_bridge_resource_idx(res));
+		} else {
+			pci_warn(dev, "ignore the current offset of BAR %llx-%llx\n",
+				 l64, l64 + sz64 - 1);
+			res->start = 0;
+			res->end = sz64 - 1;
+			res->flags |= IORESOURCE_SIZEALIGN;
+		}
 	}
 
 	goto out;
@@ -3164,6 +3183,11 @@  bool pci_dev_bar_movable(struct pci_dev *dev, struct resource *res)
 	if (region.start == 0xa0000)
 		return false;
 
+	if (res->start &&
+	    !(res->flags & IORESOURCE_IO) &&
+	    (dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+		return false;
+
 	if (!dev->driver && !res->child)
 		return true;