diff mbox

[v7,5/6] drm/i915/guc: Check the locking status of GuC WOPCM registers

Message ID 1516325372-24448-5-git-send-email-yaodong.li@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jackie Li Jan. 19, 2018, 1:29 a.m. UTC
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

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

Comments

Chris Wilson Feb. 1, 2018, 7:41 a.m. UTC | #1
Quoting Jackie Li (2018-01-19 01:29:31)
> 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
> 
> Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
> Signed-off-by: Jackie Li <yaodong.li@intel.com>
> ---
> +static inline bool __reg_locked(struct drm_i915_private *dev_priv,
> +                               i915_reg_t reg)
> +{
> +       return !!(I915_READ(reg) & GUC_WOPCM_REG_LOCKED);

Why the double cast to bool?
-Chris
Jackie Li Feb. 1, 2018, 10:27 p.m. UTC | #2
On 01/31/2018 11:41 PM, Chris Wilson wrote:
>
>>   - Fixed patch format issues
>>
>> Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
>> Cc: Chris Wilson <chris@chris-wilson.co.uk>
>> Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
>> Signed-off-by: Jackie Li <yaodong.li@intel.com>
>> ---
>> +static inline bool __reg_locked(struct drm_i915_private *dev_priv,
>> +                               i915_reg_t reg)
>> +{
>> +       return !!(I915_READ(reg) & GUC_WOPCM_REG_LOCKED);
> Why the double cast to bool?
My thought was the code would be clearer to use bool as the return type and
have a explicit cast to bool. Are you suggesting I should use a return 
type such as *int*
and remove the double cast?

Regards,
-Jackie
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index ea35911..7ed0c17 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -118,7 +118,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 9109be7..ee2b33ba 100644
--- a/drivers/gpu/drm/i915/intel_guc_reg.h
+++ b/drivers/gpu/drm/i915/intel_guc_reg.h
@@ -66,6 +66,7 @@ 
 #define   UOS_MOVE			  (1<<4)
 #define   START_DMA			  (1<<0)
 #define DMA_GUC_WOPCM_OFFSET		_MMIO(0xc340)
+#define   DMA_GUC_WOPCM_OFFSET_MASK	  (0xffffc000)
 #define   HUC_LOADING_AGENT_VCR		  (0<<1)
 #define   HUC_LOADING_AGENT_GUC		  (1<<1)
 #define GUC_MAX_IDLE_COUNT		_MMIO(0xC3E4)
@@ -75,7 +76,8 @@ 
 
 /* Defines WOPCM space available to GuC firmware */
 #define GUC_WOPCM_SIZE			_MMIO(0xc050)
-#define GUC_WOPCM_SIZE_MASK		(0xfffff000)
+#define   GUC_WOPCM_SIZE_MASK		  (0xfffff000)
+#define GUC_WOPCM_REG_LOCKED		BIT(0)
 
 /* GuC addresses above GUC_GGTT_TOP also don't map through the GTT */
 #define GUC_GGTT_TOP			0xFEE00000
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.c b/drivers/gpu/drm/i915/intel_guc_wopcm.c
index ed3096c..236fc32 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c
@@ -89,6 +89,53 @@  static inline int guc_wopcm_size_check(struct intel_guc *guc)
 	return 0;
 }
 
+static inline bool __reg_locked(struct drm_i915_private *dev_priv,
+				i915_reg_t reg)
+{
+	return !!(I915_READ(reg) & GUC_WOPCM_REG_LOCKED);
+}
+
+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;
+
+	size = I915_READ(GUC_WOPCM_SIZE) & GUC_WOPCM_SIZE_MASK;
+	offset = I915_READ(DMA_GUC_WOPCM_OFFSET);
+
+	guc_loads_huc = !!(offset & HUC_LOADING_AGENT_GUC);
+	offset &= DMA_GUC_WOPCM_OFFSET_MASK;
+
+	return guc_loads_huc && (size == guc->wopcm.size) &&
+		(offset == guc->wopcm.offset);
+}
+
 /*
  * intel_guc_wopcm_init() - Initialize the GuC WOPCM partition.
  * @guc: intel guc.
@@ -107,8 +154,7 @@  int intel_guc_wopcm_init(struct intel_guc *guc, u32 guc_fw_size,
 	u32 offset, size, top;
 	int err;
 
-	if (guc->wopcm.valid)
-		return 0;
+	GEM_BUG_ON(guc->wopcm.flags & INTEL_GUC_WOPCM_VALID);
 
 	if (!guc_fw_size)
 		return -EINVAL;
@@ -147,10 +193,49 @@  int intel_guc_wopcm_init(struct intel_guc *guc, u32 guc_fw_size,
 	if (err)
 		return err;
 
-	guc->wopcm.valid = true;
+	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.
+ */
+void intel_guc_wopcm_init_hw(struct intel_guc *guc)
+{
+	u32 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) {
+		/*
+		 * 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;
+
+		GEM_BUG_ON(!(guc->wopcm.flags & INTEL_GUC_WOPCM_HW_UPDATED));
+		return;
+	}
+
+	/* Always update registers when GuC WOPCM is not locked. */
+	guc_wopcm_hw_update(guc);
+
+out:
+	guc->wopcm.flags |= INTEL_GUC_WOPCM_HW_UPDATED;
+}
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h b/drivers/gpu/drm/i915/intel_guc_wopcm.h
index 5306175..f075e37 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.h
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h
@@ -47,11 +47,22 @@  struct intel_guc;
 #define GEN9_GUC_WOPCM_DELTA		4
 #define GEN9_GUC_WOPCM_OFFSET		(0x24000)
 
+/* 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.
+ * @size: size of GuC WOPCM for GuC firmware.
+ * @top: start of Non GuC WOPCM memory.
+ * @flags: bitmap of INTEL_GUC_WOPCM_<foo>.
+ */
 struct intel_guc_wopcm {
 	u32 offset;
 	u32 size;
 	u32 top;
-	bool valid;
+	u32 flags;
 };
 
 /*
@@ -69,5 +80,6 @@  static inline void intel_guc_wopcm_init_early(struct intel_guc_wopcm *wopcm)
 }
 
 int intel_guc_wopcm_init(struct intel_guc *guc, u32 guc_size, u32 huc_size);
+void intel_guc_wopcm_init_hw(struct intel_guc *guc);
 
 #endif
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index b5a08d1..0ee49b5 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -326,10 +326,7 @@  int intel_uc_init_hw(struct drm_i915_private *dev_priv)
 	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);
+	intel_guc_wopcm_init_hw(guc);
 
 	/* WaEnableuKernelHeaderValidFix:skl */
 	/* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */