[Intel-gfx] [PATCH 1/2] drm/i915: Add code to accept valid locked WOPCM register values

Jackie Li yaodong.li at intel.com
Tue Mar 20 23:18:46 UTC 2018


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.

Signed-off-by: Jackie Li <yaodong.li at intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko at intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble at intel.com>
Cc: Michał Winiarski <michal.winiarski at intel.com>
Cc: John Spotswood <john.a.spotswood at intel.com>
---
 drivers/gpu/drm/i915/intel_wopcm.c | 183 +++++++++++++++++++++++++++++++------
 1 file changed, 157 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c
index 4117886..fbd8aba 100644
--- a/drivers/gpu/drm/i915/intel_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_wopcm.c
@@ -138,6 +138,53 @@ static inline int check_hw_restriction(struct drm_i915_private *i915,
 	return err;
 }
 
+static inline u32 calculate_min_guc_wopcm_base(u32 huc_fw_size)
+{
+	return ALIGN(huc_fw_size + WOPCM_RESERVED_SIZE,
+		     GUC_WOPCM_OFFSET_ALIGNMENT);
+}
+
+static inline u32 calculate_min_guc_wopcm_size(u32 guc_fw_size)
+{
+	return guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
+}
+
+static inline int calculate_max_guc_wopcm_size(struct intel_wopcm *wopcm,
+					       u32 guc_wopcm_base,
+					       u32 *guc_wopcm_size)
+{
+	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
+	u32 ctx_rsvd = context_reserved_size(i915);
+
+	if (guc_wopcm_base >= wopcm->size ||
+	    (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_MASK;
+
+	return 0;
+}
+
+static inline int verify_calculated_values(struct drm_i915_private *i915,
+					   u32 guc_fw_size, u32 huc_fw_size,
+					   u32 guc_wopcm_base,
+					   u32 guc_wopcm_size)
+{
+	if (guc_wopcm_size < calculate_min_guc_wopcm_size(guc_fw_size)) {
+		DRM_ERROR("Need %uKiB WOPCM for GuC FW, %uKiB available.\n",
+			  calculate_min_guc_wopcm_size(guc_fw_size),
+			  guc_wopcm_size / 1024);
+		return -E2BIG;
+	}
+
+	return check_hw_restriction(i915, guc_wopcm_base, guc_wopcm_size,
+				    huc_fw_size);
+}
+
 /**
  * intel_wopcm_init() - Initialize the WOPCM structure.
  * @wopcm: pointer to intel_wopcm.
@@ -155,10 +202,8 @@ 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;
 	int err;
 
 	GEM_BUG_ON(!wopcm->size);
@@ -175,30 +220,17 @@ int intel_wopcm_init(struct intel_wopcm *wopcm)
 		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;
+	guc_wopcm_base = calculate_min_guc_wopcm_base(huc_fw_size);
+	err = calculate_max_guc_wopcm_size(wopcm, guc_wopcm_base,
+					   &guc_wopcm_size);
+	if (err)
+		return err;
 
 	DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
 			 guc_wopcm_base / 1024, guc_wopcm_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(i915, guc_fw_size, huc_fw_size,
+				       guc_wopcm_base, guc_wopcm_size);
 	if (err)
 		return err;
 
@@ -208,6 +240,92 @@ int intel_wopcm_init(struct intel_wopcm *wopcm)
 	return 0;
 }
 
+static inline int verify_locked_values(struct intel_wopcm *wopcm,
+				       u32 guc_wopcm_base, u32 guc_wopcm_size)
+{
+	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);
+
+	/*
+	 * Locked GuC WOPCM base and size could be any values are large enough
+	 * to lead to a wrap around after performing add operations.
+	 */
+	if (guc_wopcm_base >= wopcm->size) {
+		DRM_ERROR("Locked base value %uKiB >= WOPCM size %uKiB.\n",
+			  guc_wopcm_base / 1024,
+			  wopcm->size / 1024);
+		return -E2BIG;
+	}
+
+	if (guc_wopcm_size >= wopcm->size) {
+		DRM_ERROR("Locked size value %uKiB >= WOPCM size %uKiB.\n",
+			  guc_wopcm_base / 1024,
+			  wopcm->size / 1024);
+		return -E2BIG;
+	}
+
+	if (guc_wopcm_base + guc_wopcm_size + ctx_rsvd > wopcm->size) {
+		DRM_ERROR("Need %uKiB WOPCM in total, %uKiB available.\n",
+			  (guc_wopcm_base + guc_wopcm_size + ctx_rsvd) / 1024,
+			  (wopcm->size) / 1024);
+		return -E2BIG;
+	}
+
+	if (guc_wopcm_base < calculate_min_guc_wopcm_base(huc_fw_size)) {
+		DRM_ERROR("Need %uKiB WOPCM for HuC FW, %uKiB available.\n",
+			  calculate_min_guc_wopcm_base(huc_fw_size) / 1024,
+			  guc_wopcm_base / 1024);
+		return -E2BIG;
+	}
+
+	return verify_calculated_values(i915, guc_fw_size, huc_fw_size,
+					guc_wopcm_base, guc_wopcm_size);
+}
+
+static inline int verify_locked_values_and_update(struct intel_wopcm *wopcm)
+{
+	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 size_val = I915_READ(GUC_WOPCM_SIZE);
+	u32 offset_val = I915_READ(DMA_GUC_WOPCM_OFFSET);
+	bool offset_reg_locked = offset_val & GUC_WOPCM_OFFSET_VALID;
+	bool size_reg_locked = size_val & GUC_WOPCM_SIZE_LOCKED;
+	u32 guc_wopcm_base = offset_val & GUC_WOPCM_OFFSET_MASK;
+	u32 guc_wopcm_size = size_val & GUC_WOPCM_SIZE_MASK;
+	int err;
+
+	if (!offset_reg_locked && !size_reg_locked)
+		return 0;
+
+	if (guc_wopcm_base == wopcm->guc.base &&
+	    guc_wopcm_size == wopcm->guc.size)
+		return 0;
+
+	if (!offset_reg_locked)
+		guc_wopcm_base = calculate_min_guc_wopcm_base(huc_fw_size);
+
+	if (!size_reg_locked) {
+		err = calculate_max_guc_wopcm_size(wopcm, guc_wopcm_base,
+						   &guc_wopcm_size);
+		if (err)
+			return err;
+	}
+
+	DRM_DEBUG_DRIVER("Recalculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
+			 guc_wopcm_base / 1024, guc_wopcm_size / 1024);
+
+	err = verify_locked_values(wopcm, guc_wopcm_base, guc_wopcm_size);
+	if (err)
+		return err;
+
+	wopcm->guc.size = guc_wopcm_size;
+	wopcm->guc.base = guc_wopcm_base;
+
+	return 0;
+}
+
 static inline int write_and_verify(struct drm_i915_private *dev_priv,
 				   i915_reg_t reg, u32 val, u32 mask,
 				   u32 locked_bit)
@@ -231,7 +349,8 @@ static inline int write_and_verify(struct drm_i915_private *dev_priv,
  * will verify the register values to make sure the registers are locked with
  * correct values.
  *
- * Return: 0 on success. -EIO if registers were locked with incorrect values.
+ * Return: 0 on success. Minus error code if registered were locked with
+ * incorrect values.-EIO if registers failed to lock with correct values.
  */
 int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
 {
@@ -247,27 +366,39 @@ int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
 	GEM_BUG_ON(!wopcm->guc.size);
 	GEM_BUG_ON(!wopcm->guc.base);
 
+	err = verify_locked_values_and_update(wopcm);
+	if (err) {
+		DRM_ERROR("WOPCM registers are locked with invalid values.\n");
+		goto err_out;
+	}
+
 	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)
+	if (err) {
+		DRM_ERROR("Failed to lock GUC_WOPCM_SIZE with %#x.\n",
+			  wopcm->guc.size);
 		goto err_out;
+	}
 
 	huc_agent = USES_HUC(dev_priv) ? HUC_LOADING_AGENT_GUC : 0;
 	mask = GUC_WOPCM_OFFSET_MASK | GUC_WOPCM_OFFSET_VALID | huc_agent;
 	err = write_and_verify(dev_priv, DMA_GUC_WOPCM_OFFSET,
 			       wopcm->guc.base | huc_agent, mask,
 			       GUC_WOPCM_OFFSET_VALID);
-	if (err)
+	if (err) {
+		DRM_ERROR("Failed to lock DMA_GUC_WOPCM_OFFSET with %#x.\n",
+			  wopcm->guc.base);
 		goto err_out;
+	}
 
 	return 0;
 
 err_out:
-	DRM_ERROR("Failed to init WOPCM registers:\n");
 	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



More information about the Intel-gfx mailing list