Commit 07b79ed1 authored by Alex Deucher's avatar Alex Deucher Committed by Sasha Levin

drm/radeon: handle vfct with multiple vbios images

[ Upstream commit a882f5de ]

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>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
parent 1efa8e2a
...@@ -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 ||
vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) ||
vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) ||
vhdr->VendorID != rdev->pdev->vendor ||
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 += sizeof(VFCT_IMAGE_HEADER);
DRM_ERROR("ACPI VFCT image truncated\n"); if (offset > tbl_size) {
goto out_unmap; DRM_ERROR("ACPI VFCT image header truncated\n");
} return false;
}
rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); offset += vhdr->ImageLength;
ret = !!rdev->bios; if (offset > tbl_size) {
DRM_ERROR("ACPI VFCT image truncated\n");
return false;
}
if (vhdr->ImageLength &&
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