In current code, we only compare the locked WOPCM register values with the
calculated values. However, we can continue loading GuC/HuC firmware if the
locked (or partially locked) values were valid for current GuC/HuC firmware
sizes.

This patch added a new code path to verify whether the locked register
values can be used for GuC/HuC firmware loading, it will recalculate the
verify the new values if these registers were partially locked, so that we
won't fail the GuC/HuC firmware loading even if the locked register values
are different from the calculated ones.

v2:
 - Update WOPCM register only if it's not locked

v4:
 - Fixed typo in code comments (Michal)
 - Refined function names and parameters (Michal)
 - Avoided duplicated function calls (Michal)
 - Refined register updating ordering (Michal)

Signed-off-by: Jackie Li <yaodong...@intel.com>
Cc: Michal Wajdeczko <michal.wajdec...@intel.com>
Cc: Sagar Arun Kamble <sagar.a.kam...@intel.com>
Cc: Michal Winiarski <michal.winiar...@intel.com>
Cc: John Spotswood <john.a.spotsw...@intel.com>
Cc: Joonas Lahtinen <joonas.lahti...@linux.intel.com>
Reviewed-by: John Spotswood <john.a.spotsw...@intel.com>
---
 drivers/gpu/drm/i915/intel_wopcm.c | 251 ++++++++++++++++++++++++++++---------
 1 file changed, 194 insertions(+), 57 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_wopcm.c 
b/drivers/gpu/drm/i915/intel_wopcm.c
index b1c08ca..8fdcc48 100644
--- a/drivers/gpu/drm/i915/intel_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_wopcm.c
@@ -51,6 +51,8 @@
 /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
 #define GUC_WOPCM_STACK_RESERVED       (8 * 1024)
 
+/* GuC WOPCM Size value needs to be aligned to 4KB. */
+#define GUC_WOPCM_SIZE_ALIGNMENT       (1UL << GUC_WOPCM_SIZE_SHIFT)
 /* GuC WOPCM Offset value needs to be aligned to 16KB. */
 #define GUC_WOPCM_OFFSET_ALIGNMENT     (1UL << GUC_WOPCM_OFFSET_SHIFT)
 
@@ -86,60 +88,104 @@ static inline u32 context_reserved_size(struct 
drm_i915_private *i915)
                return 0;
 }
 
-static inline int gen9_check_dword_gap(u32 guc_wopcm_base, u32 guc_wopcm_size)
+static inline int gen9_check_dword_gap(struct intel_wopcm *wopcm)
 {
-       u32 offset;
+       u32 gen9_min_guc_wopcm_size = wopcm->guc.base + GEN9_GUC_WOPCM_OFFSET +
+                                     sizeof(u32);
 
        /*
         * GuC WOPCM size shall be at least a dword larger than the offset from
         * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
         * due to hardware limitation on Gen9.
         */
-       offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
-       if (offset > guc_wopcm_size ||
-           (guc_wopcm_size - offset) < sizeof(u32)) {
+       if (wopcm->guc.size < gen9_min_guc_wopcm_size) {
                DRM_ERROR("GuC WOPCM size %uKiB is too small. %uKiB needed.\n",
-                         guc_wopcm_size / 1024,
-                         (u32)(offset + sizeof(u32)) / 1024);
+                         wopcm->guc.size / 1024,
+                         gen9_min_guc_wopcm_size / 1024);
                return -E2BIG;
        }
 
        return 0;
 }
 
-static inline int gen9_check_huc_fw_fits(u32 guc_wopcm_size, u32 huc_fw_size)
+static inline int gen9_check_huc_fw_fits(struct intel_wopcm *wopcm,
+                                        u32 huc_fw_size)
 {
+       u32 available_guc_wopcm = wopcm->guc.size - GUC_WOPCM_RESERVED;
+
        /*
         * On Gen9 & CNL A0, hardware requires the total available GuC WOPCM
         * size to be larger than or equal to HuC firmware size. Otherwise,
         * firmware uploading would fail.
         */
-       if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
+       if (huc_fw_size > available_guc_wopcm) {
                DRM_ERROR("HuC FW (%uKiB) won't fit in GuC WOPCM (%uKiB).\n",
-                         huc_fw_size / 1024,
-                         (guc_wopcm_size - GUC_WOPCM_RESERVED) / 1024);
+                         huc_fw_size / 1024, available_guc_wopcm / 1024);
                return -E2BIG;
        }
 
        return 0;
 }
 
-static inline int check_hw_restriction(struct drm_i915_private *i915,
-                                      u32 guc_wopcm_base, u32 guc_wopcm_size,
+static inline int check_hw_restriction(struct intel_wopcm *wopcm,
                                       u32 huc_fw_size)
 {
+       struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
        int err = 0;
 
        if (IS_GEN9(i915))
-               err = gen9_check_dword_gap(guc_wopcm_base, guc_wopcm_size);
+               err = gen9_check_dword_gap(wopcm);
 
        if (!err &&
            (IS_GEN9(i915) || IS_CNL_REVID(i915, CNL_REVID_A0, CNL_REVID_A0)))
-               err = gen9_check_huc_fw_fits(guc_wopcm_size, huc_fw_size);
+               err = gen9_check_huc_fw_fits(wopcm, huc_fw_size);
 
        return err;
 }
 
+static inline u32 fw_usable_wopcm_size(struct intel_wopcm *wopcm)
+{
+       struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
+       u32 ctx_rsvd_size = context_reserved_size(i915);
+
+       return wopcm->size - ctx_rsvd_size;
+}
+
+static inline u32 min_guc_wopcm_size(u32 guc_fw_size)
+{
+       return ALIGN(guc_fw_size + GUC_WOPCM_RESERVED +
+                    GUC_WOPCM_STACK_RESERVED, GUC_WOPCM_SIZE_ALIGNMENT);
+}
+
+static inline u32 min_guc_wopcm_base(u32 huc_fw_size)
+{
+       return ALIGN(huc_fw_size + WOPCM_RESERVED_SIZE,
+                    GUC_WOPCM_OFFSET_ALIGNMENT);
+}
+
+static inline u32 max_guc_wopcm_size(u32 max_available_wopcm_size,
+                                    u32 min_guc_wopcm_base)
+{
+       if (max_available_wopcm_size > min_guc_wopcm_base)
+               return (max_available_wopcm_size - min_guc_wopcm_base) &
+                      GUC_WOPCM_SIZE_MASK;
+       return 0;
+}
+
+static inline int verify_calculated_values(struct intel_wopcm *wopcm,
+                                          u32 guc_fw_size, u32 huc_fw_size)
+{
+       u32 min_size = min_guc_wopcm_size(guc_fw_size);
+
+       if (wopcm->guc.size < min_size) {
+               DRM_ERROR("Need %uKiB WOPCM for GuC FW, %uKiB available.\n",
+                         min_size, wopcm->guc.size / 1024);
+               return -E2BIG;
+       }
+
+       return check_hw_restriction(wopcm, huc_fw_size);
+}
+
 /**
  * intel_wopcm_init() - Initialize the WOPCM structure.
  * @wopcm: pointer to intel_wopcm.
@@ -150,66 +196,135 @@ static inline int check_hw_restriction(struct 
drm_i915_private *i915,
  * size. It will fail the WOPCM init if any of these checks were failed, so 
that
  * the following GuC firmware uploading would be aborted.
  *
- * Return: 0 on success, non-zero error code on failure.
+ * Return: 0 on success. Minus error code if registers were locked with
+ * incorrect values. -EIO if registers failed to lock with correct values.
  */
 int intel_wopcm_init(struct intel_wopcm *wopcm)
 {
        struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
        u32 guc_fw_size = intel_uc_fw_get_upload_size(&i915->guc.fw);
        u32 huc_fw_size = intel_uc_fw_get_upload_size(&i915->huc.fw);
-       u32 ctx_rsvd = context_reserved_size(i915);
-       u32 guc_wopcm_base;
-       u32 guc_wopcm_size;
-       u32 guc_wopcm_rsvd;
+       u32 fw_usable_size = fw_usable_wopcm_size(wopcm);
        int err;
 
        GEM_BUG_ON(!wopcm->size);
 
-       if (guc_fw_size >= wopcm->size) {
+       if (guc_fw_size >= fw_usable_size) {
                DRM_ERROR("GuC FW (%uKiB) is too big to fit in WOPCM.",
                          guc_fw_size / 1024);
                return -E2BIG;
        }
 
-       if (huc_fw_size >= wopcm->size) {
+       if (huc_fw_size >= fw_usable_size) {
                DRM_ERROR("HuC FW (%uKiB) is too big to fit in WOPCM.",
                          huc_fw_size / 1024);
                return -E2BIG;
        }
 
-       guc_wopcm_base = ALIGN(huc_fw_size + WOPCM_RESERVED_SIZE,
-                              GUC_WOPCM_OFFSET_ALIGNMENT);
-       if ((guc_wopcm_base + ctx_rsvd) >= wopcm->size) {
-               DRM_ERROR("GuC WOPCM base (%uKiB) is too big.\n",
-                         guc_wopcm_base / 1024);
-               return -E2BIG;
-       }
-
-       guc_wopcm_size = wopcm->size - guc_wopcm_base - ctx_rsvd;
-       guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
+       wopcm->guc.base = min_guc_wopcm_base(huc_fw_size);
+       wopcm->guc.size = max_guc_wopcm_size(fw_usable_size, wopcm->guc.base);
 
        DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
-                        guc_wopcm_base / 1024, guc_wopcm_size / 1024);
+                        wopcm->guc.base / 1024,
+                        (wopcm->guc.base + wopcm->guc.size) / 1024);
 
-       guc_wopcm_rsvd = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
-       if ((guc_fw_size + guc_wopcm_rsvd) > guc_wopcm_size) {
-               DRM_ERROR("Need %uKiB WOPCM for GuC, %uKiB available.\n",
-                         (guc_fw_size + guc_wopcm_rsvd) / 1024,
-                         guc_wopcm_size / 1024);
-               return -E2BIG;
-       }
-
-       err = check_hw_restriction(i915, guc_wopcm_base, guc_wopcm_size,
-                                  huc_fw_size);
+       err = verify_calculated_values(wopcm, guc_fw_size, huc_fw_size);
        if (err)
                return err;
 
-       wopcm->guc.base = guc_wopcm_base;
-       wopcm->guc.size = guc_wopcm_size;
-
        return 0;
 }
 
+static inline int verify_locked_values(struct intel_wopcm *wopcm)
+{
+       struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
+       u32 guc_fw_size = intel_uc_fw_get_upload_size(&i915->guc.fw);
+       u32 huc_fw_size = intel_uc_fw_get_upload_size(&i915->huc.fw);
+       u32 fw_usable_size = fw_usable_wopcm_size(wopcm);
+       u32 min_base = min_guc_wopcm_base(huc_fw_size);
+
+       /*
+        * Locked GuC WOPCM base and size could be any values which are large
+        * enough to lead to a wrap around after add operations.
+        */
+       if (wopcm->guc.base >= fw_usable_size) {
+               DRM_ERROR("Locked base value %uKiB >= WOPCM size %uKiB.\n",
+                         wopcm->guc.base / 1024,
+                         wopcm->size / 1024);
+               return -E2BIG;
+       }
+
+       if (wopcm->guc.size >= fw_usable_size) {
+               DRM_ERROR("Locked size value %uKiB >= WOPCM size %uKiB.\n",
+                         wopcm->guc.size / 1024,
+                         wopcm->size / 1024);
+               return -E2BIG;
+       }
+
+       if (wopcm->guc.base + wopcm->guc.size  > fw_usable_size) {
+               DRM_ERROR("Need %uKiB WOPCM for firmware, %uKiB available.\n",
+                         (wopcm->guc.base + wopcm->guc.size) / 1024,
+                         fw_usable_size / 1024);
+               return -E2BIG;
+       }
+
+       if (wopcm->guc.base < min_base) {
+               DRM_ERROR("Need %uKiB WOPCM for HuC FW, %uKiB available.\n",
+                         min_base / 1024,
+                         wopcm->guc.base / 1024);
+               return -E2BIG;
+       }
+
+       return verify_calculated_values(wopcm, guc_fw_size, huc_fw_size);
+}
+
+/*
+ * Check WOPCM write-once register status. if locked with different values
+ * from the calculated ones, then this function will try to recalculate the
+ * values when possible (partially locked) and verify the recalculated value.
+ */
+static inline int check_locked_values(struct intel_wopcm *wopcm,
+                                     bool *update_size_reg,
+                                     bool *update_offset_reg)
+{
+       struct drm_i915_private *dev_priv = wopcm_to_i915(wopcm);
+       u32 huc_fw_size = intel_uc_fw_get_upload_size(&dev_priv->huc.fw);
+       u32 fw_usable_size = fw_usable_wopcm_size(wopcm);
+       u32 offset_val = I915_READ(DMA_GUC_WOPCM_OFFSET);
+       u32 size_val = I915_READ(GUC_WOPCM_SIZE);
+       bool offset_reg_locked = offset_val & GUC_WOPCM_OFFSET_VALID;
+       bool size_reg_locked = size_val & GUC_WOPCM_SIZE_LOCKED;
+
+       *update_size_reg = !size_reg_locked;
+       *update_offset_reg = !offset_reg_locked;
+
+       if (!offset_reg_locked && !size_reg_locked)
+               return 0;
+
+       if (wopcm->guc.base == (offset_val & GUC_WOPCM_OFFSET_MASK) &&
+           wopcm->guc.size == (size_val & GUC_WOPCM_SIZE_MASK))
+               return 0;
+
+       if (USES_HUC(dev_priv) && offset_reg_locked &&
+           !(offset_val & HUC_LOADING_AGENT_GUC)) {
+               DRM_ERROR("HUC_LOADING_AGENT_GUC isn't locked for USES_HUC.\n");
+               return -EIO;
+       }
+
+       if (!offset_reg_locked)
+               wopcm->guc.base = min_guc_wopcm_base(huc_fw_size);
+
+       if (!size_reg_locked)
+               wopcm->guc.size = max_guc_wopcm_size(fw_usable_size,
+                                                    wopcm->guc.base);
+
+       DRM_DEBUG_DRIVER("Recalculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
+                        wopcm->guc.base / 1024,
+                        (wopcm->guc.base + wopcm->guc.size) / 1024);
+
+       return verify_locked_values(wopcm);
+}
+
 static inline int write_and_verify(struct drm_i915_private *dev_priv,
                                   i915_reg_t reg, u32 val, u32 mask,
                                   u32 locked_bit)
@@ -238,6 +353,8 @@ static inline int write_and_verify(struct drm_i915_private 
*dev_priv,
 int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
 {
        struct drm_i915_private *dev_priv = wopcm_to_i915(wopcm);
+       bool update_size_reg;
+       bool update_offset_reg;
        int err;
 
        if (!USES_GUC(dev_priv))
@@ -247,19 +364,38 @@ int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
        GEM_BUG_ON(!wopcm->guc.size);
        GEM_BUG_ON(!wopcm->guc.base);
 
-       err = write_and_verify(dev_priv, GUC_WOPCM_SIZE, wopcm->guc.size,
-                              GUC_WOPCM_SIZE_MASK | GUC_WOPCM_SIZE_LOCKED,
-                              GUC_WOPCM_SIZE_LOCKED);
-       if (err)
+       err = check_locked_values(wopcm, &update_size_reg, &update_offset_reg);
+       if (err) {
+               DRM_ERROR("WOPCM registers are locked with invalid values.\n");
                goto err_out;
+       }
 
-       err = write_and_verify(dev_priv, DMA_GUC_WOPCM_OFFSET,
-                              wopcm->guc.base | HUC_LOADING_AGENT_GUC,
-                              GUC_WOPCM_OFFSET_MASK | HUC_LOADING_AGENT_GUC |
-                              GUC_WOPCM_OFFSET_VALID,
-                              GUC_WOPCM_OFFSET_VALID);
-       if (err)
-               goto err_out;
+       if (update_offset_reg) {
+               err = write_and_verify(dev_priv, DMA_GUC_WOPCM_OFFSET,
+                                      wopcm->guc.base | HUC_LOADING_AGENT_GUC,
+                                      GUC_WOPCM_OFFSET_MASK |
+                                      HUC_LOADING_AGENT_GUC |
+                                      GUC_WOPCM_OFFSET_VALID,
+                                      GUC_WOPCM_OFFSET_VALID);
+               if (err) {
+                       DRM_ERROR("Failed to GuC WOPCM offset with %#x.\n",
+                                 wopcm->guc.base);
+                       goto err_out;
+               }
+       }
+
+       if (update_size_reg) {
+               err = write_and_verify(dev_priv, GUC_WOPCM_SIZE,
+                                      wopcm->guc.size,
+                                      GUC_WOPCM_SIZE_MASK |
+                                      GUC_WOPCM_SIZE_LOCKED,
+                                      GUC_WOPCM_SIZE_LOCKED);
+               if (err) {
+                       DRM_ERROR("Failed to GuC WOPCM size with %#x.\n",
+                                 wopcm->guc.size);
+                       goto err_out;
+               }
+       }
 
        return 0;
 
@@ -268,6 +404,7 @@ int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
        DRM_ERROR("DMA_GUC_WOPCM_OFFSET=%#x\n",
                  I915_READ(DMA_GUC_WOPCM_OFFSET));
        DRM_ERROR("GUC_WOPCM_SIZE=%#x\n", I915_READ(GUC_WOPCM_SIZE));
+       DRM_ERROR("A system reboot is required.\n");
 
        return err;
 }
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to