drm/nouveau/bios: pointers beyond end of first image need special handling
authorBen Skeggs <bskeggs@redhat.com>
Wed, 22 Jun 2016 02:41:04 +0000 (12:41 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Thu, 14 Jul 2016 01:53:25 +0000 (11:53 +1000)
Makes common the code that was previously used by the PMU table parsing,
as it appears other tables need this too.

Not much of an idea what this is all about...

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios.h
drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/image.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c

index 6137de829b8fbd10b830da023cfe152282653aec..a72f3290528a4a0c8bddca75c3d8f3b6e186c95c 100644 (file)
@@ -7,6 +7,9 @@ struct nvkm_bios {
        u32 size;
        u8 *data;
 
+       u32 image0_size;
+       u32 imaged_addr;
+
        u32 bmp_offset;
        u32 bit_offset;
 
index db5fa009a6194e0f091a7186c4b5fb02f8f5c7ce..f3c30b2a788e8534fe72c23729779afd5e39d573 100644 (file)
 #include <subdev/bios.h>
 #include <subdev/bios/bmp.h>
 #include <subdev/bios/bit.h>
+#include <subdev/bios/image.h>
 
 static bool
 nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size)
 {
+       u32 p = *addr;
+
+       if (*addr > bios->image0_size && bios->imaged_addr) {
+               *addr -= bios->image0_size;
+               *addr += bios->imaged_addr;
+       }
+
        if (unlikely(*addr + size >= bios->size)) {
-               nvkm_error(&bios->subdev, "OOB %d %08x\n", size, *addr);
+               nvkm_error(&bios->subdev, "OOB %d %08x %08x\n", size, p, *addr);
                return false;
        }
+
        return true;
 }
 
@@ -134,8 +143,9 @@ int
 nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios)
 {
        struct nvkm_bios *bios;
+       struct nvbios_image image;
        struct bit_entry bit_i;
-       int ret;
+       int ret, idx = 0;
 
        if (!(bios = *pbios = kzalloc(sizeof(*bios), GFP_KERNEL)))
                return -ENOMEM;
@@ -145,6 +155,19 @@ nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios)
        if (ret)
                return ret;
 
+       /* Some tables have weird pointers that need adjustment before
+        * they're dereferenced.  I'm not entirely sure why...
+        */
+       if (nvbios_image(bios, idx++, &image)) {
+               bios->image0_size = image.size;
+               while (nvbios_image(bios, idx++, &image)) {
+                       if (image.type == 0xe0) {
+                               bios->imaged_addr = image.base;
+                               break;
+                       }
+               }
+       }
+
        /* detect type of vbios we're dealing with */
        bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
                                          "\xff\x7f""NV\0", 5);
index 74b14cf093082a1208ee707f1df35a04c822cde0..1dbff7aeafec2ecf2006d588e3656c70071fb1c2 100644 (file)
@@ -68,11 +68,16 @@ nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image)
 bool
 nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image)
 {
+       u32 imaged_addr = bios->imaged_addr;
        memset(image, 0x00, sizeof(*image));
+       bios->imaged_addr = 0;
        do {
                image->base += image->size;
-               if (image->last || !nvbios_imagen(bios, image))
+               if (image->last || !nvbios_imagen(bios, image)) {
+                       bios->imaged_addr = imaged_addr;
                        return false;
+               }
        } while(idx--);
+       bios->imaged_addr = imaged_addr;
        return true;
 }
index c268e5afe852f35af49f9e65c80556671802d10a..b4a308f3cf7b0c461515a9904e62280b245d70a9 100644 (file)
 #include <subdev/bios/image.h>
 #include <subdev/bios/pmu.h>
 
-static u32
-weirdo_pointer(struct nvkm_bios *bios, u32 data)
-{
-       struct nvbios_image image;
-       int idx = 0;
-       if (nvbios_image(bios, idx++, &image)) {
-               data -= image.size;
-               while (nvbios_image(bios, idx++, &image)) {
-                       if (image.type == 0xe0)
-                               return image.base + data;
-               }
-       }
-       return 0;
-}
-
 u32
 nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
 {
@@ -50,7 +35,7 @@ nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
        if (!bit_entry(bios, 'p', &bit_p)) {
                if (bit_p.version == 2 && bit_p.length >= 4)
                        data = nvbios_rd32(bios, bit_p.offset + 0x00);
-               if ((data = weirdo_pointer(bios, data))) {
+               if (data) {
                        *ver = nvbios_rd08(bios, data + 0x00); /* maybe? */
                        *hdr = nvbios_rd08(bios, data + 0x01);
                        *len = nvbios_rd08(bios, data + 0x02);
@@ -97,8 +82,7 @@ nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info)
        u32 data;
        memset(info, 0x00, sizeof(*info));
        while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) {
-               if ( pmuE.type == type &&
-                   (data = weirdo_pointer(bios, pmuE.data))) {
+               if (pmuE.type == type && (data = pmuE.data)) {
                        info->init_addr_pmu = nvbios_rd32(bios, data + 0x08);
                        info->args_addr_pmu = nvbios_rd32(bios, data + 0x0c);
                        info->boot_addr     = data + 0x30;