Commit 2f96e8e3 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/bios: pointers beyond end of first image need special handling

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: default avatarBen Skeggs <bskeggs@redhat.com>
parent 4d4e9907
...@@ -7,6 +7,9 @@ struct nvkm_bios { ...@@ -7,6 +7,9 @@ struct nvkm_bios {
u32 size; u32 size;
u8 *data; u8 *data;
u32 image0_size;
u32 imaged_addr;
u32 bmp_offset; u32 bmp_offset;
u32 bit_offset; u32 bit_offset;
......
...@@ -26,14 +26,23 @@ ...@@ -26,14 +26,23 @@
#include <subdev/bios.h> #include <subdev/bios.h>
#include <subdev/bios/bmp.h> #include <subdev/bios/bmp.h>
#include <subdev/bios/bit.h> #include <subdev/bios/bit.h>
#include <subdev/bios/image.h>
static bool static bool
nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size) 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)) { 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 false;
} }
return true; return true;
} }
...@@ -134,8 +143,9 @@ int ...@@ -134,8 +143,9 @@ int
nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios) nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios)
{ {
struct nvkm_bios *bios; struct nvkm_bios *bios;
struct nvbios_image image;
struct bit_entry bit_i; struct bit_entry bit_i;
int ret; int ret, idx = 0;
if (!(bios = *pbios = kzalloc(sizeof(*bios), GFP_KERNEL))) if (!(bios = *pbios = kzalloc(sizeof(*bios), GFP_KERNEL)))
return -ENOMEM; return -ENOMEM;
...@@ -145,6 +155,19 @@ nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios) ...@@ -145,6 +155,19 @@ nvkm_bios_new(struct nvkm_device *device, int index, struct nvkm_bios **pbios)
if (ret) if (ret)
return 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 */ /* detect type of vbios we're dealing with */
bios->bmp_offset = nvbios_findstr(bios->data, bios->size, bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
"\xff\x7f""NV\0", 5); "\xff\x7f""NV\0", 5);
......
...@@ -68,11 +68,16 @@ nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image) ...@@ -68,11 +68,16 @@ nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image)
bool bool
nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image) nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image)
{ {
u32 imaged_addr = bios->imaged_addr;
memset(image, 0x00, sizeof(*image)); memset(image, 0x00, sizeof(*image));
bios->imaged_addr = 0;
do { do {
image->base += image->size; 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; return false;
}
} while(idx--); } while(idx--);
bios->imaged_addr = imaged_addr;
return true; return true;
} }
...@@ -26,21 +26,6 @@ ...@@ -26,21 +26,6 @@
#include <subdev/bios/image.h> #include <subdev/bios/image.h>
#include <subdev/bios/pmu.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 u32
nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) 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) ...@@ -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_entry(bios, 'p', &bit_p)) {
if (bit_p.version == 2 && bit_p.length >= 4) if (bit_p.version == 2 && bit_p.length >= 4)
data = nvbios_rd32(bios, bit_p.offset + 0x00); data = nvbios_rd32(bios, bit_p.offset + 0x00);
if ((data = weirdo_pointer(bios, data))) { if (data) {
*ver = nvbios_rd08(bios, data + 0x00); /* maybe? */ *ver = nvbios_rd08(bios, data + 0x00); /* maybe? */
*hdr = nvbios_rd08(bios, data + 0x01); *hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02); *len = nvbios_rd08(bios, data + 0x02);
...@@ -97,8 +82,7 @@ nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info) ...@@ -97,8 +82,7 @@ nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info)
u32 data; u32 data;
memset(info, 0x00, sizeof(*info)); memset(info, 0x00, sizeof(*info));
while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) { while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) {
if ( pmuE.type == type && if (pmuE.type == type && (data = pmuE.data)) {
(data = weirdo_pointer(bios, pmuE.data))) {
info->init_addr_pmu = nvbios_rd32(bios, data + 0x08); info->init_addr_pmu = nvbios_rd32(bios, data + 0x08);
info->args_addr_pmu = nvbios_rd32(bios, data + 0x0c); info->args_addr_pmu = nvbios_rd32(bios, data + 0x0c);
info->boot_addr = data + 0x30; info->boot_addr = data + 0x30;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment