From: Andre Eikmeyer <[email protected]>

fwupd reads /dev/drm_dp_aux* while the GPU is runtime suspended. The AUX
device holds no runtime PM reference, so the transfer touched registers of
the powered off GPU and caused a CPU CATTER.

Signed-off-by: Andre Eikmeyer <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 7261317d4..b099bf520 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -24,6 +24,7 @@
  *
  */
 
+#include <linux/pm_runtime.h>
 #include <linux/vmalloc.h>
 #include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_dp_mst_helper.h>
@@ -111,6 +112,20 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct 
drm_dp_aux *aux,
        if (WARN_ON(msg->size > 16))
                return -E2BIG;
 
+       ddc = TO_DM_AUX(aux)->ddc_service;
+       adev = ddc->ctx->driver_context;
+
+       /*
+        * User space reaches this through the drm_dp_aux_dev character device
+        * without a runtime PM reference. Refuse the transfer while GMUX keeps
+        * the GPU powered off, a register access then hangs the PEG link.
+        */
+       pm_runtime_get_noresume(adev->dev);
+       if (READ_ONCE(adev->gmux_off)) {
+               pm_runtime_put_noidle(adev->dev);
+               return -EBUSY;
+       }
+
        payload.address = msg->address;
        payload.data = msg->buffer;
        payload.length = msg->size;
@@ -126,13 +141,13 @@ STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct 
drm_dp_aux *aux,
        result = dc_link_aux_transfer_raw(TO_DM_AUX(aux)->ddc_service, &payload,
                                      &operation_result);
 
+       pm_runtime_put_autosuspend(adev->dev);
+
        /*
         * w/a on certain intel platform where hpd is unexpected to pull low 
during
         * 1st sideband message transaction by return AUX_RET_ERROR_HPD_DISCON
         * aux transaction is succuess in such case, therefore bypass the error
         */
-       ddc = TO_DM_AUX(aux)->ddc_service;
-       adev = ddc->ctx->driver_context;
        if (adev->dm.aux_hpd_discon_quirk) {
                if (msg->address == DP_SIDEBAND_MSG_DOWN_REQ_BASE &&
                        operation_result == AUX_RET_ERROR_HPD_DISCON) {
-- 
2.55.0

Reply via email to