[Intel-gfx] [PATCH v3 3/4] drm/i915: Add code to accept valid locked WOPCM register values
Jackie Li
yaodong.li at intel.com
Tue Apr 10 00:42:19 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.
v2:
- Update WOPCM register only if it's not locked
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: Michal Winiarski <michal.winiarski at intel.com>
Cc: John Spotswood <john.a.spotswood at intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
---
drivers/gpu/drm/i915/intel_wopcm.c | 217 +++++++++++++++++++++++++++++++------
1 file changed, 185 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c
index b1c08ca..fa8d2be 100644
--- a/drivers/gpu/drm/i915/intel_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_wopcm.c
@@ -140,6 +140,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.
@@ -157,10 +204,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);
@@ -177,35 +222,121 @@ 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);
+ 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_base + guc_wopcm_size) / 1024);
+
+ err = verify_calculated_values(i915, guc_fw_size, huc_fw_size,
+ guc_wopcm_base, guc_wopcm_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,
+ 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 which 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;
}
- guc_wopcm_size = wopcm->size - guc_wopcm_base - ctx_rsvd;
- guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
+ 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;
+ }
- DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
- guc_wopcm_base / 1024, guc_wopcm_size / 1024);
+ 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;
+ }
- 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);
+ 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;
}
- err = check_hw_restriction(i915, guc_wopcm_base, guc_wopcm_size,
- huc_fw_size);
+ 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,
+ 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 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;
+
+ *update_size_reg = !size_reg_locked;
+ *update_offset_reg = !offset_reg_locked;
+
+ 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 (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)
+ 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_base + guc_wopcm_size) / 1024);
+
+ err = verify_locked_values(wopcm, guc_wopcm_base, guc_wopcm_size);
if (err)
return err;
- wopcm->guc.base = guc_wopcm_base;
wopcm->guc.size = guc_wopcm_size;
+ wopcm->guc.base = guc_wopcm_base;
return 0;
}
@@ -233,11 +364,14 @@ 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)
{
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,27 +381,46 @@ 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 = verify_locked_values_and_update(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_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;
+ }
+ }
+ 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;
+ }
+ }
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