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

Jackie Li yaodong.li at intel.com
Mon Feb 12 19:22:23 UTC 2018


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

This patch moves the GuC WOPCM registers updating code into
intel_guc_wopcm.c and adds check before and after the update to GuC WOPCM
registers so that we can 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)
 - Added code to set and validate the HuC_LOADING_AGENT_GUC bit in GuC
   WOPCM offset register based on the presence of HuC firmware (Michal)
 - Use bit fields instead of macros for GuC WOPCM flags (Michal)

v10:
 - Refined variable names, removed reduntant comments (Joonas)
 - Introduced lockable_reg to handle the write once register write and
   propagate the write error to caller (Joonas)
 - Removed hw_updated flag and only relies on real hardware status

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_reg.h   |   2 +
 drivers/gpu/drm/i915/intel_guc_wopcm.c | 153 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_guc_wopcm.h |  11 ++-
 drivers/gpu/drm/i915/intel_uc.c        |   9 +-
 4 files changed, 165 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h
index c482df5..f7070ae 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 b7be236..7f13539 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c
@@ -103,6 +103,12 @@ void intel_guc_wopcm_init_early(struct intel_guc_wopcm *guc_wopcm)
 	guc_wopcm->top = WOPCM_DEFAULT_SIZE;
 }
 
+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_wopcm: pointer to intel_guc_wopcm.
@@ -121,12 +127,13 @@ 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_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 = context_reserved_size(guc);
 	u32 offset, size, top;
 	int err;
 
+	GEM_BUG_ON(guc->wopcm.valid);
+
 	if (!guc_fw_size)
 		return -EINVAL;
 
@@ -155,6 +162,7 @@ int intel_guc_wopcm_init(struct intel_guc_wopcm *guc_wopcm, u32 guc_fw_size,
 	guc->wopcm.offset = offset;
 	guc->wopcm.size = size;
 	guc->wopcm.top = top;
+	guc->wopcm.load_huc_fw = huc_fw_size > 0;
 
 	err = guc_wopcm_size_check(guc, huc_fw_size);
 	if (err)
@@ -167,3 +175,144 @@ int intel_guc_wopcm_init(struct intel_guc_wopcm *guc_wopcm, u32 guc_fw_size,
 
 	return 0;
 }
+
+struct lockable_reg {
+	const char *name;
+	struct intel_guc *guc;
+	i915_reg_t reg;
+	u32 reg_val;
+	u32 lock_bit;
+	bool (*validate)(struct lockable_reg *lreg, u32 reg_val);
+};
+
+#define DEFINE_LOCKABLE_REG(var, rg, lb, func)	\
+	struct lockable_reg var = {	\
+		.name = #rg,	\
+		.guc = guc,	\
+		.reg = rg,	\
+		.reg_val = 0,	\
+		.lock_bit = lb,	\
+		.validate = func,	\
+	}
+
+static inline u32 lockable_reg_read(struct lockable_reg *lreg)
+{
+	struct drm_i915_private *dev_priv = guc_to_i915(lreg->guc);;
+
+	lreg->reg_val = I915_READ(lreg->reg);
+
+	return lreg->reg_val;
+}
+
+static inline bool lockable_reg_validate(struct lockable_reg *lreg, bool update)
+{
+	GEM_BUG_ON(!lreg->validate);
+
+	if (update)
+		lockable_reg_read(lreg);
+
+	return lreg->validate(lreg, lreg->reg_val);
+}
+
+static inline bool lockable_reg_locked(struct lockable_reg *lreg)
+{
+	u32 reg_val = lockable_reg_read(lreg);
+
+	return reg_val & lreg->lock_bit;;
+}
+
+static inline bool lockable_reg_locked_and_valid(struct lockable_reg *lreg)
+{
+	if (lockable_reg_locked(lreg)) {
+		if (lockable_reg_validate(lreg, false))
+			return true;
+
+		return false;
+	}
+
+	return false;
+}
+
+static inline bool lockable_reg_write(struct lockable_reg *lreg, u32 val)
+{
+	struct drm_i915_private *dev_priv = guc_to_i915(lreg->guc);
+
+	if (lockable_reg_locked(lreg)) {
+		if (lockable_reg_validate(lreg, false))
+			goto out;
+
+		DRM_ERROR("Register %s was locked with invalid value\n",
+			  lreg->name);
+
+		return false;
+	}
+
+	I915_WRITE(lreg->reg, val);
+
+	if (!lockable_reg_locked_and_valid(lreg)) {
+		DRM_ERROR("Failed lock Register %s\n", lreg->name);
+		return false;
+	}
+
+out:
+	DRM_DEBUG_DRIVER("Write-once register %s locked with valid value %#x\n",
+			 lreg->name, lreg->reg_val);
+
+	return true;
+}
+
+static inline bool size_reg_value_validate(struct lockable_reg *lreg,
+					   u32 reg_val)
+{
+	struct intel_guc_wopcm *guc_wopcm = &lreg->guc->wopcm;
+	u32 size = reg_val & GUC_WOPCM_SIZE_MASK;
+
+	return size == guc_wopcm->size;
+}
+
+static inline bool offset_reg_value_validate(struct lockable_reg *lreg,
+					     u32 reg_val)
+{
+	struct intel_guc_wopcm *guc_wopcm = &lreg->guc->wopcm;
+	bool guc_loads_huc = reg_val & HUC_LOADING_AGENT_GUC;
+	u32 offset = reg_val & GUC_WOPCM_OFFSET_MASK;
+
+	if (guc_wopcm->load_huc_fw && !guc_loads_huc)
+		return false;
+
+	return offset == guc_wopcm->offset;
+}
+
+/**
+ * intel_guc_wopcm_init_hw() - Setup GuC WOPCM registers.
+ * @guc_wopcm: intel_guc_wopcm.
+ *
+ * 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);
+	DEFINE_LOCKABLE_REG(guc_wopcm_size_reg, GUC_WOPCM_SIZE, BIT(0),
+			    size_reg_value_validate);
+	DEFINE_LOCKABLE_REG(guc_wopcm_offset_reg, DMA_GUC_WOPCM_OFFSET, BIT(0),
+			    offset_reg_value_validate);
+	u32 reg_val;
+
+	GEM_BUG_ON(!guc->wopcm.valid);
+
+	reg_val = guc_wopcm->size;
+	if (!lockable_reg_write(&guc_wopcm_size_reg, reg_val))
+		return -EIO;
+
+	reg_val = guc_wopcm->offset;
+	if (guc->wopcm.load_huc_fw)
+		reg_val |= HUC_LOADING_AGENT_GUC;
+	if (!lockable_reg_write(&guc_wopcm_offset_reg, reg_val))
+		return -EIO;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h b/drivers/gpu/drm/i915/intel_guc_wopcm.h
index 487fe3f..068a13c 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.h
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h
@@ -86,7 +86,9 @@ struct intel_guc;
  * @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.
+ * @valid: whether the values in this struct are valid.
+ * @hw_updated: GuC WOPCM registers has been updated with values in this struct.
+ * @need_load_huc_fw: whether need to configure GuC to load HuC firmware.
  *
  * 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.
@@ -95,11 +97,14 @@ struct intel_guc_wopcm {
 	u32 offset;
 	u32 size;
 	u32 top;
-	u32 valid;
+
+	/* GuC WOPCM flags below. */
+	u32 valid:1;
+	u32 load_huc_fw:1;
 };
 
 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