aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2017-01-25 15:33:44 -0500
committerAlex Deucher <alexander.deucher@amd.com>2017-01-27 12:20:41 -0500
commita882f5de402ded769af74fbf276132f9c175049c (patch)
treee6b857c5cb4b626b87faf330af3bf86e06c444aa /drivers
parent689957b12b6315c63bd8cce879e2b259a8a4b666 (diff)
drm/radeon: handle vfct with multiple vbios images
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: Alex Deucher <alexander.deucher@amd.com> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c66
1 files changed, 36 insertions, 30 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index c829cfb02fc4..00cfb5d2875f 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -596,52 +596,58 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev)
596#ifdef CONFIG_ACPI 596#ifdef CONFIG_ACPI
597static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) 597static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
598{ 598{
599 bool ret = false;
600 struct acpi_table_header *hdr; 599 struct acpi_table_header *hdr;
601 acpi_size tbl_size; 600 acpi_size tbl_size;
602 UEFI_ACPI_VFCT *vfct; 601 UEFI_ACPI_VFCT *vfct;
603 GOP_VBIOS_CONTENT *vbios; 602 unsigned offset;
604 VFCT_IMAGE_HEADER *vhdr;
605 603
606 if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) 604 if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
607 return false; 605 return false;
608 tbl_size = hdr->length; 606 tbl_size = hdr->length;
609 if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { 607 if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
610 DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); 608 DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n");
611 goto out_unmap; 609 return false;
612 } 610 }
613 611
614 vfct = (UEFI_ACPI_VFCT *)hdr; 612 vfct = (UEFI_ACPI_VFCT *)hdr;
615 if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { 613 offset = vfct->VBIOSImageOffset;
616 DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
617 goto out_unmap;
618 }
619 614
620 vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); 615 while (offset < tbl_size) {
621 vhdr = &vbios->VbiosHeader; 616 GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset);
622 DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", 617 VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader;
623 vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction,
624 vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength);
625
626 if (vhdr->PCIBus != rdev->pdev->bus->number ||
627 vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) ||
628 vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) ||
629 vhdr->VendorID != rdev->pdev->vendor ||
630 vhdr->DeviceID != rdev->pdev->device) {
631 DRM_INFO("ACPI VFCT table is not for this card\n");
632 goto out_unmap;
633 }
634 618
635 if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { 619 offset += sizeof(VFCT_IMAGE_HEADER);
636 DRM_ERROR("ACPI VFCT image truncated\n"); 620 if (offset > tbl_size) {
637 goto out_unmap; 621 DRM_ERROR("ACPI VFCT image header truncated\n");
638 } 622 return false;
623 }
639 624
640 rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); 625 offset += vhdr->ImageLength;
641 ret = !!rdev->bios; 626 if (offset > tbl_size) {
627 DRM_ERROR("ACPI VFCT image truncated\n");
628 return false;
629 }
630
631 if (vhdr->ImageLength &&
632 vhdr->PCIBus == rdev->pdev->bus->number &&
633 vhdr->PCIDevice == PCI_SLOT(rdev->pdev->devfn) &&
634 vhdr->PCIFunction == PCI_FUNC(rdev->pdev->devfn) &&
635 vhdr->VendorID == rdev->pdev->vendor &&
636 vhdr->DeviceID == rdev->pdev->device) {
637 rdev->bios = kmemdup(&vbios->VbiosContent,
638 vhdr->ImageLength,
639 GFP_KERNEL);
640
641 if (!rdev->bios) {
642 kfree(rdev->bios);
643 return false;
644 }
645 return true;
646 }
647 }
642 648
643out_unmap: 649 DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
644 return ret; 650 return false;
645} 651}
646#else 652#else
647static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) 653static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev)