Commit 71e91ff6 authored by Alex Deucher's avatar Alex Deucher Committed by Ben Hutchings

drm/radeon: handle vfct with multiple vbios images

commit a882f5de upstream.

The vfct table can contain multiple vbios images if the
platform contains multiple GPUs. Noticed by netkas on
phoronix forums.  This patch fixes those platforms.
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
[bwh: Backported to 3.16: adjust context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent ac274045
...@@ -597,51 +597,57 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) ...@@ -597,51 +597,57 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev)
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
{ {
bool ret = false;
struct acpi_table_header *hdr; struct acpi_table_header *hdr;
acpi_size tbl_size; acpi_size tbl_size;
UEFI_ACPI_VFCT *vfct; UEFI_ACPI_VFCT *vfct;
GOP_VBIOS_CONTENT *vbios; unsigned offset;
VFCT_IMAGE_HEADER *vhdr;
if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size))) if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size)))
return false; return false;
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n");
goto out_unmap; return false;
} }
vfct = (UEFI_ACPI_VFCT *)hdr; vfct = (UEFI_ACPI_VFCT *)hdr;
if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { offset = vfct->VBIOSImageOffset;
DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
goto out_unmap;
}
vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); while (offset < tbl_size) {
vhdr = &vbios->VbiosHeader; GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset);
DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader;
vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction,
vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength);
if (vhdr->PCIBus != rdev->pdev->bus->number || offset += sizeof(VFCT_IMAGE_HEADER);
vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) || if (offset > tbl_size) {
vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) || DRM_ERROR("ACPI VFCT image header truncated\n");
vhdr->VendorID != rdev->pdev->vendor || return false;
vhdr->DeviceID != rdev->pdev->device) { }
DRM_INFO("ACPI VFCT table is not for this card\n");
goto out_unmap;
}
if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { offset += vhdr->ImageLength;
DRM_ERROR("ACPI VFCT image truncated\n"); if (offset > tbl_size) {
goto out_unmap; DRM_ERROR("ACPI VFCT image truncated\n");
} return false;
}
rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); if (vhdr->ImageLength &&
ret = !!rdev->bios; vhdr->PCIBus == rdev->pdev->bus->number &&
vhdr->PCIDevice == PCI_SLOT(rdev->pdev->devfn) &&
vhdr->PCIFunction == PCI_FUNC(rdev->pdev->devfn) &&
vhdr->VendorID == rdev->pdev->vendor &&
vhdr->DeviceID == rdev->pdev->device) {
rdev->bios = kmemdup(&vbios->VbiosContent,
vhdr->ImageLength,
GFP_KERNEL);
if (!rdev->bios) {
kfree(rdev->bios);
return false;
}
return true;
}
}
out_unmap: DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
return ret; return false;
} }
#else #else
static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
......
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