drm/i915/bios: do not discard address space
authorLucas De Marchi <lucas.demarchi@intel.com>
Tue, 26 Nov 2019 22:51:08 +0000 (14:51 -0800)
committerLucas De Marchi <lucas.demarchi@intel.com>
Mon, 2 Dec 2019 21:54:21 +0000 (13:54 -0800)
When we map the VBT through pci_map_rom() we may not be allowed
to simply discard the address space and go on reading the memory.
That doesn't work on my test system, but by dumping the rom via
sysfs I can can get the correct vbt. So change our find_vbt() to do
the same as done by pci_read_rom(), i.e. use memcpy_fromio().

v2: the just the minimal changes by not bothering with the unaligned io
reads: this can be done on top (from Ville and Jani)

v3: drop const in function return since now we are copying the vbt,
rather than just finding it

Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191126225110.8127-2-lucas.demarchi@intel.com
drivers/gpu/drm/i915/display/intel_bios.c

index 70e644082bec36371b3a0eee919e2f33fd061100..ce19f434ab377f012aff570e20b4e36a85b72a59 100644 (file)
@@ -1925,28 +1925,52 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size)
        return vbt;
 }
 
-static const struct vbt_header *find_vbt(void __iomem *oprom, size_t size)
+static struct vbt_header *copy_vbt(void __iomem *oprom, size_t size)
 {
+       void __iomem *p = NULL;
+       struct vbt_header *vbt;
+       u16 vbt_size;
        size_t i;
 
        /* Scour memory looking for the VBT signature. */
        for (i = 0; i + 4 < size; i++) {
-               void *vbt;
-
                if (ioread32(oprom + i) != *((const u32 *)"$VBT"))
                        continue;
 
-               /*
-                * This is the one place where we explicitly discard the address
-                * space (__iomem) of the BIOS/VBT.
-                */
-               vbt = (void __force *)oprom + i;
-               if (intel_bios_is_valid_vbt(vbt, size - i))
-                       return vbt;
-
+               p = oprom + i;
+               size -= i;
                break;
        }
 
+       if (!p)
+               return NULL;
+
+       if (sizeof(struct vbt_header) > size) {
+               DRM_DEBUG_DRIVER("VBT header incomplete\n");
+               return NULL;
+       }
+
+       vbt_size = ioread16(p + offsetof(struct vbt_header, vbt_size));
+       if (vbt_size > size) {
+               DRM_DEBUG_DRIVER("VBT incomplete (vbt_size overflows)\n");
+               return NULL;
+       }
+
+       /* The rest will be validated by intel_bios_is_valid_vbt() */
+       vbt = kmalloc(vbt_size, GFP_KERNEL);
+       if (!vbt)
+               return NULL;
+
+       memcpy_fromio(vbt, p, vbt_size);
+
+       if (!intel_bios_is_valid_vbt(vbt, vbt_size))
+               goto err_free_vbt;
+
+       return vbt;
+
+err_free_vbt:
+       kfree(vbt);
+
        return NULL;
 }
 
@@ -1982,7 +2006,7 @@ void intel_bios_init(struct drm_i915_private *dev_priv)
                if (!oprom)
                        goto out;
 
-               vbt = find_vbt(oprom, size);
+               vbt = copy_vbt(oprom, size);
                if (!vbt)
                        goto out;
 
@@ -2020,6 +2044,9 @@ out:
 
        if (oprom)
                pci_unmap_rom(pdev, oprom);
+
+       if (vbt != dev_priv->opregion.vbt)
+               kfree(vbt);
 }
 
 /**