[Intel-gfx] [PATCH v3 3/4] drm/i915: Add code to accept valid locked WOPCM register values
John Spotswood
john.a.spotswood at intel.com
Fri Apr 13 23:34:30 UTC 2018
On Mon, 2018-04-09 at 17:42 -0700, Jackie Li wrote:
> 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>
Reviewed-by: John Spotswood <john.a.spotswood at 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;
> }
More information about the Intel-gfx
mailing list