⬢ DragonFlyBSD Kernel Audit
DF-5467 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/pci/vga_pci.c b/sys/bus/pci/vga_pci.c
index 1111111..2222222 100644
--- a/sys/bus/pci/vga_pci.c
+++ b/sys/bus/pci/vga_pci.c
@@ -58,6 +58,7 @@
 	device_t	vga_msi_child;	/* Child driver using MSI. */
 	struct vga_resource vga_bars[PCIR_MAX_BAR_0 + 1];
 	struct vga_resource vga_bios;
+	int	vga_bios_shadow;	/* BIOS mapped via C0000 shadow copy. */
 };
 
 SYSCTL_DECL(_hw_pci);
@@ -183,8 +184,10 @@
 		 */
 
 		*size = VGA_PCI_BIOS_SHADOW_SIZE;
+		((struct vga_pci_softc *)device_get_softc(dev))->vga_bios_shadow = 1;
 		return (pmap_mapbios(VGA_PCI_BIOS_SHADOW_ADDR, *size));
 	}
+	((struct vga_pci_softc *)device_get_softc(dev))->vga_bios_shadow = 0;
 #endif
 
 	pcib = device_get_parent(device_get_parent(dev));
@@ -285,11 +288,25 @@
 	}
 
 #if defined(__amd64__) || defined(__i386__)
-	if (vga_pci_is_boot_display(dev)) {
-		/* We mapped the BIOS shadow copy located at 0xC0000. */
-		pmap_unmapdev((vm_offset_t)bios, VGA_PCI_BIOS_SHADOW_SIZE);
+	{
+		struct vga_pci_softc *sc = device_get_softc(dev);
 
-		return;
+		/*
+		 * Unmap the mapping kind that map_bios() actually created,
+		 * NOT what a fresh is_boot_display() evaluation says now:
+		 * map_bios() itself flips that predicate (it sets
+		 * PCIB_BCR_VGA_ENABLE on the parent bridge), so re-evaluating
+		 * it here can unmap a ROM-BAR mapping with the 128KB shadow
+		 * size and corrupt the kernel page tables / kernel_map.
+		 */
+		if (sc->vga_bios_shadow) {
+			/* We mapped the BIOS shadow copy located at 0xC0000. */
+			sc->vga_bios_shadow = 0;
+			pmap_unmapdev((vm_offset_t)bios,
+			    VGA_PCI_BIOS_SHADOW_SIZE);
+
+			return;
+		}
 	}
 #endif
 	switch(pci_read_config(dev, PCIR_HDRTYPE, 1)) {