[PATCH v9 6/7] drm/i915/guc: Check the locking status of GuC WOPCM registers

Jackie Li yaodong.li at intel.com
Thu Feb 8 03:51:52 UTC 2018


GuC WOPCM registers are write-once registers. Current driver code accesses
these registers without checking the accessibility to these registers, this
will lead unpredictable driver behaviors if these registers were touch by
other components (such as faulty BIOS code).

This patch moves the GuC WOPCM register updating operations into
intel_guc_wopcm.c and adds checks before and after the write to GuC WOPCM
registers to make sure the driver is in a known state before and after
writing to these write-once registers.

v6:
 - Made sure module reloading won't bug the kernel while doing
   locking status checking

v7:
 - Fixed patch format issues

v8:
 - Fixed coding style issue on register lock bit macro definition (Sagar)

v9:
 - Avoided to use redundant !! to cast uint to bool (Chris)
 - Return error code instead of GEM_BUG_ON for locked with invalid register
   values case (Sagar)
 - Updated guc_wopcm_hw_init to use guc_wopcm as first parameter (Michal)

Cc: Michal Wajdeczko <michal.wajdeczko at intel.com>
Cc: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
Signed-off-by: Jackie Li <yaodong.li at intel.com>
---
 drivers/gpu/drm/i915/intel_guc.h       |   2 +-
 drivers/gpu/drm/i915/intel_guc_reg.h   |   2 +
 drivers/gpu/drm/i915/intel_guc_wopcm.c | 109 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_guc_wopcm.h |  10 ++-
 drivers/gpu/drm/i915/intel_uc.c        |   9 ++-
 5 files changed, 120 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 06f315e..cb1703b 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -122,7 +122,7 @@ static inline u32 intel_guc_ggtt_offset(struct intel_guc *guc,
 {
 	u32 offset = i915_ggtt_offset(vma);
 
-	GEM_BUG_ON(!guc->wopcm.valid);
+	GEM_BUG_ON(!(guc->wopcm.flags & INTEL_GUC_WOPCM_VALID));
 	GEM_BUG_ON(offset < guc->wopcm.top);
 	GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP));
 
diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h
index de4f78b..170d9cd 100644
--- a/drivers/gpu/drm/i915/intel_guc_reg.h
+++ b/drivers/gpu/drm/i915/intel_guc_reg.h
@@ -66,6 +66,8 @@
 #define   UOS_MOVE			  (1<<4)
 #define   START_DMA			  (1<<0)
 #define DMA_GUC_WOPCM_OFFSET		_MMIO(0xc340)
+#define   GUC_WOPCM_OFFSET_SHIFT	14
+#define   GUC_WOPCM_OFFSET_MASK		  (0x3ffff << GUC_WOPCM_OFFSET_SHIFT)
 #define   HUC_LOADING_AGENT_VCR		  (0<<1)
 #define   HUC_LOADING_AGENT_GUC		  (1<<1)
 #define GUC_MAX_IDLE_COUNT		_MMIO(0xC3E4)
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.c b/drivers/gpu/drm/i915/intel_guc_wopcm.c
index ff6b3eb..212bb7f 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c
@@ -88,6 +88,61 @@ static inline int guc_wopcm_size_check(struct intel_guc *guc, u32 huc_fw_size)
 	return 0;
 }
 
+static inline bool __reg_locked(struct drm_i915_private *dev_priv,
+				i915_reg_t reg)
+{
+	/* Set of bit-0 means this write-once register is locked. */
+	return I915_READ(reg) & BIT(0);
+}
+
+static inline bool guc_wopcm_locked(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_i915(guc);
+	bool size_reg_locked = __reg_locked(i915, GUC_WOPCM_SIZE);
+	bool offset_reg_locked = __reg_locked(i915, DMA_GUC_WOPCM_OFFSET);
+
+	return size_reg_locked && offset_reg_locked;
+}
+
+static inline void guc_wopcm_hw_update(struct intel_guc *guc)
+{
+	struct drm_i915_private *dev_priv = guc_to_i915(guc);
+
+	/* GuC WOPCM registers should be unlocked at this point. */
+	GEM_BUG_ON(__reg_locked(dev_priv, GUC_WOPCM_SIZE));
+	GEM_BUG_ON(__reg_locked(dev_priv, DMA_GUC_WOPCM_OFFSET));
+
+	I915_WRITE(GUC_WOPCM_SIZE, guc->wopcm.size);
+	I915_WRITE(DMA_GUC_WOPCM_OFFSET,
+		   guc->wopcm.offset | HUC_LOADING_AGENT_GUC);
+
+	GEM_BUG_ON(!__reg_locked(dev_priv, GUC_WOPCM_SIZE));
+	GEM_BUG_ON(!__reg_locked(dev_priv, DMA_GUC_WOPCM_OFFSET));
+}
+
+static inline bool guc_wopcm_regs_valid(struct intel_guc *guc)
+{
+	struct drm_i915_private *dev_priv = guc_to_i915(guc);
+	u32 size, offset;
+	bool guc_loads_huc;
+
+	/* GuC WOPCM size should be always multiple of 4K pages. */
+	size = I915_READ(GUC_WOPCM_SIZE) & PAGE_MASK;
+	offset = I915_READ(DMA_GUC_WOPCM_OFFSET);
+
+	guc_loads_huc = offset & HUC_LOADING_AGENT_GUC;
+	offset &= GUC_WOPCM_OFFSET_MASK;
+
+	return guc_loads_huc && (size == guc->wopcm.size) &&
+		(offset == guc->wopcm.offset);
+}
+
+static inline
+struct intel_guc *guc_wopcm_to_guc(struct intel_guc_wopcm *guc_wopcm)
+{
+	return container_of(guc_wopcm, struct intel_guc, wopcm);
+}
+
 /**
  * intel_guc_wopcm_init() - Initialize the GuC WOPCM.
  * @guc: intel guc.
@@ -106,12 +161,13 @@ static inline int guc_wopcm_size_check(struct intel_guc *guc, u32 huc_fw_size)
 int intel_guc_wopcm_init(struct intel_guc_wopcm *guc_wopcm, u32 guc_fw_size,
 			 u32 huc_fw_size)
 {
-	struct intel_guc *guc =
-		container_of(guc_wopcm, struct intel_guc, wopcm);
+	struct intel_guc *guc = guc_wopcm_to_guc(guc_wopcm);
 	u32 reserved = guc_wopcm_context_reserved_size(guc);
 	u32 offset, size, top;
 	int err;
 
+	GEM_BUG_ON(guc->wopcm.flags & INTEL_GUC_WOPCM_VALID);
+
 	if (!guc_fw_size)
 		return -EINVAL;
 
@@ -151,10 +207,57 @@ int intel_guc_wopcm_init(struct intel_guc_wopcm *guc_wopcm, u32 guc_fw_size,
 	if (err)
 		return err;
 
-	guc->wopcm.valid = 1;
+	guc->wopcm.flags |= INTEL_GUC_WOPCM_VALID;
 
 	DRM_DEBUG_DRIVER("GuC WOPCM offset %dKB, size %dKB, top %dKB\n",
 			 offset >> 10, size >> 10, top >> 10);
 
 	return 0;
 }
+
+/**
+ * intel_guc_wopcm_init_hw() - Setup GuC WOPCM registers.
+ * @guc: intel guc.
+ *
+ * Setup the GuC WOPCM size and offset registers with the stored values. It will
+ * also check the registers locking status to determine whether these registers
+ * are unlocked and can be updated.
+ *
+ * Return: 0 on success. -EINVAL if registers were locked with incorrect values.
+ */
+int intel_guc_wopcm_init_hw(struct intel_guc_wopcm *guc_wopcm)
+{
+	struct intel_guc *guc = guc_wopcm_to_guc(guc_wopcm);
+	bool locked = guc_wopcm_locked(guc);
+
+	GEM_BUG_ON(!(guc->wopcm.flags & INTEL_GUC_WOPCM_VALID));
+
+	/*
+	 * Bug if driver hasn't updated the HW Registers and GuC WOPCM has been
+	 * locked. Return directly if WOPCM was locked and we have updated
+	 * the registers.
+	 */
+	if (locked) {
+		if (guc->wopcm.flags & INTEL_GUC_WOPCM_HW_UPDATED)
+			return 0;
+
+		/*
+		 * Mark as updated if registers contained correct values.
+		 * This will happen while reloading the driver module without
+		 * rebooting the system.
+		 */
+		if (guc_wopcm_regs_valid(guc))
+			goto out;
+
+		/* Register locked without valid values. Abort HW init. */
+		return -EINVAL;
+	}
+
+	/* Always update registers when GuC WOPCM is not locked. */
+	guc_wopcm_hw_update(guc);
+
+out:
+	guc->wopcm.flags |= INTEL_GUC_WOPCM_HW_UPDATED;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h b/drivers/gpu/drm/i915/intel_guc_wopcm.h
index 3083f16..1ba8af0 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.h
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h
@@ -79,12 +79,16 @@ struct intel_guc;
  */
 #define GEN9_GUC_WOPCM_OFFSET		(144 << 10)
 
+/* GuC WOPCM flags */
+#define INTEL_GUC_WOPCM_VALID		BIT(0)
+#define INTEL_GUC_WOPCM_HW_UPDATED	BIT(1)
+
 /**
  * intel_guc_wopcm - GuC WOPCM related settings.
  * @offset: GuC WOPCM offset from the WOPCM base.
  * @size: size of GuC WOPCM for GuC firmware.
  * @top: start of the non-GuC WOPCM memory.
- * @valid: whether this structure contains valid (1-valid, 0-invalid) info.
+ * @flags: bitmap of INTEL_GUC_WOPCM_<foo>.
  *
  * We simply use this structure to track the GuC use of WOPCM. The layout of
  * WOPCM would be defined by writing to GuC WOPCM offset and size registers.
@@ -93,7 +97,7 @@ struct intel_guc_wopcm {
 	u32 offset;
 	u32 size;
 	u32 top;
-	u32 valid;
+	u32 flags;
 };
 
 /**
@@ -112,5 +116,5 @@ static inline void intel_guc_wopcm_init_early(struct intel_guc_wopcm *guc_wopcm)
 
 int intel_guc_wopcm_init(struct intel_guc_wopcm *guc_wopcm, u32 guc_size,
 			 u32 huc_size);
-
+int intel_guc_wopcm_init_hw(struct intel_guc_wopcm *guc_wopcm);
 #endif
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index c842f36..8938096 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -343,14 +343,13 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
 
 	GEM_BUG_ON(!HAS_GUC(dev_priv));
 
+	ret = intel_guc_wopcm_init_hw(&guc->wopcm);
+	if (ret)
+		goto err_out;
+
 	guc_disable_communication(guc);
 	gen9_reset_guc_interrupts(dev_priv);
 
-	/* init WOPCM */
-	I915_WRITE(GUC_WOPCM_SIZE, guc->wopcm.size);
-	I915_WRITE(DMA_GUC_WOPCM_OFFSET,
-		   guc->wopcm.offset | HUC_LOADING_AGENT_GUC);
-
 	/* WaEnableuKernelHeaderValidFix:skl */
 	/* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */
 	if (IS_GEN9(dev_priv))
-- 
2.7.4



More information about the Intel-gfx-trybot mailing list