radeon, amdgpu: Firmware is required for DRM and KMS on R600 onward
authorBen Hutchings <ben@decadent.org.uk>
Tue, 8 Jan 2013 03:25:52 +0000 (03:25 +0000)
committerSalvatore Bonaccorso <carnil@debian.org>
Thu, 26 Sep 2019 12:19:06 +0000 (13:19 +0100)
radeon requires firmware/microcode for the GPU in all chips, but for
newer chips (apparently R600 'Evergreen' onward) it also expects
firmware for the memory controller and other sub-blocks.

radeon attempts to gracefully fall back and disable some features if
the firmware is not available, but becomes unstable - the framebuffer
and/or system memory may be corrupted, or the display may stay black.

Therefore, perform a basic check for the existence of
/lib/firmware/{radeon,amdgpu} when a device is probed, and abort if it
is missing, except for the pre-R600 case.

Gbp-Pq: Topic bugfix/all
Gbp-Pq: Name radeon-amdgpu-firmware-is-required-for-drm-and-kms-on-r600-onward.patch

drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/radeon/radeon_drv.c

index 1e2cc9d68a05def7aa26cc02a754482bd89594fe..5744014f28acbb2eab22eeb8e11ba60f16ddced5 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 #include <drm/drm_probe_helper.h>
+#include <linux/namei.h>
+#include <linux/path.h>
 
 #include "amdgpu.h"
 #include "amdgpu_irq.h"
@@ -929,6 +931,28 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
 
 static struct drm_driver kms_driver;
 
+/* Test that /lib/firmware/amdgpu is a directory (or symlink to a
+ * directory).  We could try to match the udev search path, but let's
+ * keep it simple.
+ */
+static bool amdgpu_firmware_installed(void)
+{
+#if IS_BUILTIN(CONFIG_DRM_AMDGPU)
+       /* It may be too early to tell.  Assume it's there. */
+       return true;
+#else
+       struct path path;
+
+       if (kern_path("/lib/firmware/amdgpu", LOOKUP_DIRECTORY | LOOKUP_FOLLOW,
+                     &path) == 0) {
+               path_put(&path);
+               return true;
+       }
+
+       return false;
+#endif
+}
+
 static int amdgpu_pci_probe(struct pci_dev *pdev,
                            const struct pci_device_id *ent)
 {
@@ -947,6 +971,11 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
                return -ENODEV;
        }
 
+       if (!amdgpu_firmware_installed()) {
+               DRM_ERROR("amdgpu requires firmware installed\n");
+               return -ENODEV;
+       }
+
        /* Get rid of things like offb */
        ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "amdgpudrmfb");
        if (ret)
index 2e96c886392bd1b7b2aeaafa74ef5b2750a36c20..1d54b3e5f489dfec699f2411661ac4361702a051 100644 (file)
@@ -44,6 +44,8 @@
 
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_probe_helper.h>
+#include <linux/namei.h>
+#include <linux/path.h>
 
 /*
  * KMS wrapper.
@@ -317,6 +319,28 @@ static struct drm_driver kms_driver;
 
 bool radeon_device_is_virtual(void);
 
+/* Test that /lib/firmware/radeon is a directory (or symlink to a
+ * directory).  We could try to match the udev search path, but let's
+ * keep it simple.
+ */
+static bool radeon_firmware_installed(void)
+{
+#if IS_BUILTIN(CONFIG_DRM_RADEON)
+       /* It may be too early to tell.  Assume it's there. */
+       return true;
+#else
+       struct path path;
+
+       if (kern_path("/lib/firmware/radeon", LOOKUP_DIRECTORY | LOOKUP_FOLLOW,
+                     &path) == 0) {
+               path_put(&path);
+               return true;
+       }
+
+       return false;
+#endif
+}
+
 static int radeon_pci_probe(struct pci_dev *pdev,
                            const struct pci_device_id *ent)
 {
@@ -325,6 +349,12 @@ static int radeon_pci_probe(struct pci_dev *pdev,
        if (vga_switcheroo_client_probe_defer(pdev))
                return -EPROBE_DEFER;
 
+       if ((ent->driver_data & RADEON_FAMILY_MASK) >= CHIP_R600 &&
+           !radeon_firmware_installed()) {
+               DRM_ERROR("radeon kernel modesetting for R600 or later requires firmware installed\n");
+               return -ENODEV;
+       }
+
        /* Get rid of things like offb */
        ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "radeondrmfb");
        if (ret)