Message ID | 1518479153-28429-3-git-send-email-yaodong.li@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, 13 Feb 2018 00:45:49 +0100, Jackie Li <yaodong.li@intel.com> wrote: > Hardware may have specific restrictions on GuC WOPCM offset and size. On > Gen9, the value of the GuC WOPCM size register needs to be larger than > the > value of GuC WOPCM offset register + a Gen9 specific offset (144KB) for > reserved GuC WOPCM. Fail to enforce such a restriction on GuC WOPCM size > will lead to GuC firmware execution failures. So we need add code to > verify > the GuC WOPCM offset and size to avoid any GuC failures. On the other > hand, > with current static GuC WOPCM offset and size values (512KB for both > offset > and size), the GuC WOPCM size verification will fail on Gen9 even if it > can > be fixed by lowering the GuC WOPCM offset by calculating its value based > on > HuC firmware size (which is likely less than 200KB on Gen9), so that we > can > have a GuC WOPCM size value which is large enough to pass the GuC WOPCM > size check. > > This patch updates the reserved GuC WOPCM size for RC6 context on Gen9 to > 24KB to strictly align with the Gen9 GuC WOPCM layout and add support to > return CNL specific hardware reserved GuC WOPCM size. It also adds > support > to verify the GuC WOPCM size aganist the Gen9 hardware restrictions. > Meanwhile, it provides a common way to calculate GuC WOPCM offset and > size > based on GuC and HuC firmware sizes for all GuC/HuC enabled platforms. > Currently, GuC WOPCM offset is calculated based on HuC firmware size + > reserved WOPCM size while GuC WOPCM size is set to total WOPCM size - GuC > WOPCM offset - hardware reserved GuC WOPCM size. In this case, GuC WOPCM > offset will be updated based on the size of HuC firmware while GuC WOPCM > size will be set to use all the remaining WOPCM space. > Can we add here some documentation tag like Bspec: 12690, ... as we did in https://patchwork.kernel.org/patch/10116053/ ? > v2: > - Removed intel_wopcm_init (Ville/Sagar/Joonas) > - Renamed and Moved the intel_wopcm_partition into intel_guc (Sagar) > - Removed unnecessary function calls (Joonas) > - Init GuC WOPCM partition as soon as firmware fetching is completed > > v3: > - Fixed indentation issues (Chris) > - Removed layering violation code (Chris/Michal) > - Created separat files for GuC wopcm code (Michal) > - Used inline function to avoid code duplication (Michal) > > v4: > - Preset the GuC WOPCM top during early GuC init (Chris) > - Fail intel_uc_init_hw() as soon as GuC WOPCM partitioning failed > > v5: > - Moved GuC DMA WOPCM register updating code into intel_guc_wopcm.c > - Took care of the locking status before writing to GuC DMA > Write-Once registers. (Joonas) > > v6: > - Made sure the GuC WOPCM size to be multiple of 4K (4K aligned) > > v8: > - Updated comments and fixed naming issues (Sagar/Joonas) > - Updated commit message to include more description about the hardware > restriction on GuC WOPCM size (Sagar) > > v9: > - Minor changes variable names and code comments (Sagar) > - Added detailed GuC WOPCM layout drawing (Sagar/Michal) > - Refined macro definitions to be reader friendly (Michal) > - Removed redundent check to valid flag (Michal) > - Unified first parameter for exported GuC WOPCM functions (Michal) > - Refined the name and parameter list of hardware restriction checking > functions (Michal) > > v10: > - Used shorter function name for internal functions (Joonas) > - Moved init-ealry function into c file (Joonas) > - Consolidated and removed redundant size checks (Joonas/Michal) > - Removed unnecessary unlikely() from code which is only called once > during boot (Joonas) > - More fixes to kernel-doc format and content (Michal) > - Avoided the use of PAGE_MASK for 4K pages (Michal) > - Added error log messages to error paths (Michal) > > Cc: Michal Wajdeczko <michal.wajdeczko@intel.com> > Cc: Sagar Arun Kamble <sagar.a.kamble@intel.com> > Cc: Sujaritha Sundaresan <sujaritha.sundaresan@intel.com> > Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com> > Cc: John Spotswood <john.a.spotswood@intel.com> > Cc: Oscar Mateo <oscar.mateo@intel.com> > Cc: Chris Wilson <chris@chris-wilson.co.uk> > Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> > Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com> (v8) > Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> (v9) > Signed-off-by: Jackie Li <yaodong.li@intel.com> > --- > drivers/gpu/drm/i915/i915_gem_context.c | 5 +- > drivers/gpu/drm/i915/intel_guc.c | 5 +- > drivers/gpu/drm/i915/intel_guc.h | 12 +-- > drivers/gpu/drm/i915/intel_guc_reg.h | 2 + > drivers/gpu/drm/i915/intel_guc_wopcm.c | 128 > +++++++++++++++++++++++++++++--- > drivers/gpu/drm/i915/intel_guc_wopcm.h | 72 ++++++++++++++++-- > drivers/gpu/drm/i915/intel_huc.c | 2 +- > drivers/gpu/drm/i915/intel_uc.c | 11 ++- > drivers/gpu/drm/i915/intel_uc_fw.c | 11 ++- > drivers/gpu/drm/i915/intel_uc_fw.h | 16 ++++ > 10 files changed, 231 insertions(+), 33 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_gem_context.c > b/drivers/gpu/drm/i915/i915_gem_context.c > index 3d75f48..414eab2 100644 > --- a/drivers/gpu/drm/i915/i915_gem_context.c > +++ b/drivers/gpu/drm/i915/i915_gem_context.c > @@ -312,12 +312,13 @@ __create_hw_context(struct drm_i915_private > *dev_priv, > ctx->desc_template = > default_desc_template(dev_priv, dev_priv->mm.aliasing_ppgtt); > - /* GuC requires the ring to be placed above GUC_WOPCM_TOP. If GuC is > not > + /* > + * GuC requires the ring to be placed above GuC WOPCM top. If GuC is > not > * present or not in use we still need a small bias as ring wraparound > * at offset 0 sometimes hangs. No idea why. > */ > if (USES_GUC(dev_priv)) > - ctx->ggtt_offset_bias = GUC_WOPCM_TOP; > + ctx->ggtt_offset_bias = dev_priv->guc.wopcm.top; > else > ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE; > diff --git a/drivers/gpu/drm/i915/intel_guc.c > b/drivers/gpu/drm/i915/intel_guc.c > index fdf8cb3..578e9f4 100644 > --- a/drivers/gpu/drm/i915/intel_guc.c > +++ b/drivers/gpu/drm/i915/intel_guc.c > @@ -65,6 +65,7 @@ void intel_guc_init_early(struct intel_guc *guc) > intel_guc_fw_init_early(guc); > intel_guc_ct_init_early(&guc->ct); > intel_guc_log_init_early(guc); > + intel_guc_wopcm_init_early(&guc->wopcm); > mutex_init(&guc->send_mutex); > guc->send = intel_guc_send_nop; > @@ -477,7 +478,7 @@ int intel_guc_resume(struct drm_i915_private > *dev_priv) > * This is a wrapper to create an object for use with the GuC. In order > to > * use it inside the GuC, an object needs to be pinned lifetime, so we > allocate > * both some backing storage and a range inside the Global GTT. We must > pin > - * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because > that > + * it in the GGTT somewhere other than than [0, GuC WOPCM top) because > that > * range is reserved inside GuC. > * > * Return: A i915_vma if successful, otherwise an ERR_PTR. > @@ -498,7 +499,7 @@ struct i915_vma *intel_guc_allocate_vma(struct > intel_guc *guc, u32 size) > goto err; > ret = i915_vma_pin(vma, 0, PAGE_SIZE, > - PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP); > + PIN_GLOBAL | PIN_OFFSET_BIAS | guc->wopcm.top); > if (ret) { > vma = ERR_PTR(ret); > goto err; > diff --git a/drivers/gpu/drm/i915/intel_guc.h > b/drivers/gpu/drm/i915/intel_guc.h > index 50be6de..06f315e 100644 > --- a/drivers/gpu/drm/i915/intel_guc.h > +++ b/drivers/gpu/drm/i915/intel_guc.h > @@ -49,6 +49,7 @@ struct intel_guc { > struct intel_uc_fw fw; > struct intel_guc_log log; > struct intel_guc_ct ct; > + struct intel_guc_wopcm wopcm; > /* Log snapshot if GuC errors during load */ > struct drm_i915_gem_object *load_err_log; > @@ -109,10 +110,10 @@ static inline void intel_guc_notify(struct > intel_guc *guc) > * @guc: intel guc. > * @vma: i915 graphics virtual memory area. > * > - * GuC does not allow any gfx GGTT address that falls into range [0, > WOPCM_TOP), > - * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top > address is > - * 512K. In order to exclude 0-512K address space from GGTT, all gfx > objects > - * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM. > + * GuC does not allow any gfx GGTT address that falls into range > + * [0, GuC WOPCM top), which is reserved for Boot ROM, SRAM and WOPCM. > + * All gfx objects used by GuC is pinned with PIN_OFFSET_BIAS along with > + * top of WOPCM. > * > * Return: GGTT offset that meets the GuC gfx address requirement. > */ > @@ -121,7 +122,8 @@ static inline u32 intel_guc_ggtt_offset(struct > intel_guc *guc, > { > u32 offset = i915_ggtt_offset(vma); > - GEM_BUG_ON(offset < GUC_WOPCM_TOP); > + GEM_BUG_ON(!guc->wopcm.valid); > + GEM_BUG_ON(offset < guc->wopcm.top); > GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP)); > return offset; > diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h > b/drivers/gpu/drm/i915/intel_guc_reg.h > index de4f78b..c482df5 100644 > --- a/drivers/gpu/drm/i915/intel_guc_reg.h > +++ b/drivers/gpu/drm/i915/intel_guc_reg.h > @@ -75,6 +75,8 @@ > /* Defines WOPCM space available to GuC firmware */ > #define GUC_WOPCM_SIZE _MMIO(0xc050) > +#define GUC_WOPCM_SIZE_SHIFT 12 > +#define GUC_WOPCM_SIZE_MASK (0xfffff << GUC_WOPCM_SIZE_SHIFT) > #define GEN8_GT_PM_CONFIG _MMIO(0x138140) > #define GEN9LP_GT_PM_CONFIG _MMIO(0x138140) > diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.c > b/drivers/gpu/drm/i915/intel_guc_wopcm.c > index 3972901..cf832b9 100644 > --- a/drivers/gpu/drm/i915/intel_guc_wopcm.c > +++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c > @@ -7,22 +7,128 @@ > #include "intel_guc_wopcm.h" > #include "i915_drv.h" > +static inline u32 context_reserved_size(struct intel_guc *guc) > +{ > + struct drm_i915_private *i915 = guc_to_i915(guc); > + > + if (IS_GEN9_LP(i915)) > + return BXT_GUC_WOPCM_RC6_CTX_RESERVED; > + > + return 0; > +} > + > +static inline int gen9_check_dword_gap(struct intel_guc_wopcm > *guc_wopcm) > +{ > + u32 guc_wopcm_start; > + u32 delta; > + > + /* > + * GuC WOPCM size is at least a dword larger than the offset from WOPCM > + * base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET) due > + * to hardware limitation on Gen9. > + */ > + guc_wopcm_start = guc_wopcm->offset + GEN9_GUC_WOPCM_OFFSET; > + if (guc_wopcm_start > guc_wopcm->size) > + return -E2BIG; > + > + delta = guc_wopcm->size - guc_wopcm_start; > + if (delta < sizeof(u32)) > + return -E2BIG; > + > + return 0; > +} > + > +static inline int guc_wopcm_size_check(struct intel_guc *guc) > +{ > + struct drm_i915_private *i915 = guc_to_i915(guc); > + struct intel_guc_wopcm *guc_wopcm = &guc->wopcm; > + > + if (IS_GEN9(i915)) > + return gen9_check_dword_gap(guc_wopcm); > + > + return 0; > +} > + > /** > - * intel_guc_wopcm_size() - Get the size of GuC WOPCM. > - * @guc: intel guc. > + * intel_guc_wopcm_init_early() - Early initialization of the GuC WOPCM. > + * @guc_wopcm: pointer to intel_guc_wopcm. > * > - * Get the platform specific GuC WOPCM size. > + * Setup the GuC WOPCM top to the top of the overall WOPCM. This will > guarantee > + * that the allocation of the GuC accessible objects won't fall into > WOPCM when > + * GuC partition isn't present. > * > - * Return: size of the GuC WOPCM. > */ > -u32 intel_guc_wopcm_size(struct intel_guc *guc) > +void intel_guc_wopcm_init_early(struct intel_guc_wopcm *guc_wopcm) > { > - struct drm_i915_private *i915 = guc_to_i915(guc); > - u32 size = GUC_WOPCM_TOP; > + guc_wopcm->top = WOPCM_DEFAULT_SIZE; > +} > - /* On BXT, the top of WOPCM is reserved for RC6 context */ > - if (IS_GEN9_LP(i915)) > - size -= BXT_GUC_WOPCM_RC6_RESERVED; > +/** > + * intel_guc_wopcm_init() - Initialize the GuC WOPCM. > + * @guc_wopcm: pointer to intel_guc_wopcm. > + * @guc_fw_size: size of GuC firmware. > + * @huc_fw_size: size of HuC firmware. > + * > + * Calculate the GuC WOPCM offset and size based on GuC and HuC > firmware sizes. > + * This function will set the GuC WOPCM size to the size of maximum > WOPCM > + * available for GuC. This function will also enforce platform dependent > + * hardware restrictions on GuC WOPCM offset and size. It will fail the > GuC > + * WOPCM init if any of these checks were failed, so that the following > GuC > + * firmware uploading would be aborted. > + * > + * Return: 0 on success, non-zero error code on failure. > + */ > +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); > + u32 reserved = context_reserved_size(guc); > + u32 offset, size, top; > + int err; > + > + GEM_BUG_ON(!guc_fw_size); > + > + offset = huc_fw_size + WOPCM_RESERVED_SIZE; > + > + /* Hardware requires GuC WOPCM offset to be 16K aligned. */ > + offset = ALIGN(offset, GUC_WOPCM_OFFSET_ALIGNMENT); > + if ((offset + reserved) >= WOPCM_DEFAULT_SIZE) { > + DRM_DEBUG_DRIVER("GuC WOPCM offset exceeds max WOPCM size.\n"); > + return -E2BIG; > + } > + > + top = WOPCM_DEFAULT_SIZE - offset; > + size = top - reserved; > + > + /* GuC WOPCM size must be multiple of 4K pages */ > + size &= GUC_WOPCM_SIZE_MASK; > + > + /* > + * GuC firmware size needs to be less than or equal to the size of the > + * available GuC WOPCM (total available GuC WOPCM size - reserved > size). > + * Need extra 8K stack for GuC firmware. > + */ > + reserved = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; > + if ((guc_fw_size + reserved) > size) { > + DRM_DEBUG_DRIVER("No enough GuC WOPCM for GuC firmware.\n"); > + return -E2BIG; > + } > + > + guc->wopcm.offset = offset; > + guc->wopcm.size = size; > + guc->wopcm.top = top; > + > + err = guc_wopcm_size_check(guc); > + if (err) { > + DRM_DEBUG_DRIVER("GuC WOPCM size check failed.\n"); > + return err; > + } > + > + guc->wopcm.valid = 1; > + > + DRM_DEBUG_DRIVER("GuC WOPCM offset %dKB, size %dKB, top %dKB\n", s/KB/KiB > + offset >> 10, size >> 10, top >> 10); I'm still human and prefer "offset / 1024" over binary shifts. > - return size; > + return 0; > } > diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h > b/drivers/gpu/drm/i915/intel_guc_wopcm.h > index 8c4f693..a632caa 100644 > --- a/drivers/gpu/drm/i915/intel_guc_wopcm.h > +++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h > @@ -11,15 +11,73 @@ > struct intel_guc; > -/* 512KB static offset from WOPCM base. */ > -#define GUC_WOPCM_OFFSET_VALUE (512 << 10) > /* > - * 512KB static GuC WOPCM size from GUC_WOPCM_OFFSET_VALUE to the end > of GuC > - * WOPCM. GuC addresses below GUC_WOPCM_TOP don't map through the GTT. > + * The layout of the WOPCM will be determined by GuC WOPCM size and > offset > + * registers settings. Currently, GuC WOPCM code calculates the GuC > WOPCM offset > + * and size values based on a layout as shown below: > + * > + * +=====+==>+====================+<== GuC WOPCM top > + * ^ ^ | HW contexts RSVD | > + * | | +--------------------+<== GuC WOPCM size > + * | | | | > + * | GuC | | > + * | WOPCM +--------------------+<== (Platform specific size) > + * | | | GuC FW RSVD | > + * WOPCM | +------------------- +<== (16KB) > + * | v | RSVD GuC WOPCM | > + * | +==>+====================+<== GuC WOPCM offset > + * | | RSVD WOPCM | > + * | +------------------- + > + * v | HuC FW | > + * +========>+====================+<== WOPCM Base > + * > + * GuC accessible WOPCM starts at GuC WOPCM offset and ends at GuC > WOPCM size. > + * The top part of the GuC WOPCM is reserved for hardware contexts > (e.g. RC6 > + * context). We need to keep tracking the GuC WOPCM top since hardware > requires > + * the GGTT offset of a GuC accessible GEM buffer to be larger than the > value of > + * GuC WOPCM top. The values of GuC WOPCM size and top should be set to > the > + * length from GuC WOPCM offset in bytes. > + */ Maybe this description can be converted into nice kernel-doc? And IIRC there was suggestion to move it to .c file. Also, comparing again your drawing with the spec I think it should look like this: +-------- +====================+ <== WOPCM top /|\ | HW RESERVED | | +------------------- + | | | | +-- +====================+ <== GuC WOPCM top | /|\ | | | | | | | GuC | | | WOPCM | GuC firmware | | size | | WOPCM | +--------------------+ size \|/ | GuC reserved | | +-- +====================+ <== GuC WOPCM base (offset from WOPCM base) | | WOPCM RESERVED | | +--------------------+ \|/ | HuC firmware | +-------- +====================+ <== WOPCM base > + > +/* Default WOPCM size 1MB. */ > +#define WOPCM_DEFAULT_SIZE (1 << 20) > +/* The initial 16KB WOPCM (RSVD WOPCM) is reserved. */ > +#define WOPCM_RESERVED_SIZE (16 << 10) I'm not sure that we can describe it as "initial" since it's above HuC > + > +/* GUC WOPCM offset needs to be 16KB aligned. */ > +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) > +/* 16KB reserved at the beginning of GuC WOPCM. */ > +#define GUC_WOPCM_RESERVED (16 << 10) > +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ > +#define GUC_WOPCM_STACK_RESERVED (8 << 10) > +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ > +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) Hmm, I'm still not convinced that we correctly attach it to "GuC WOPCM". In the result, maybe we should stop focusing on "intel_guc_wopcm" and define everything as "intel_wopcm" as it was earlier suggested by Joonas. Then we can define our struct to match diagram above as struct intel_wopcm { u32 size; struct { u32 base; u32 size; } guc; }; u32 intel_wopcm_guc_top(struct intel_wopcm *wopcm) { return wopcm->guc.base + wopcm->guc.size; } > + > +/* > + * GuC WOPCM starts at 144KB (GUC_WOPCM_RESERVED + 128KB reserved for > GuC > + * firmware loading) from GuC WOPCM offset on BXT. > + */ > +#define GEN9_GUC_WOPCM_OFFSET (144 << 10) Is this BXT or GEN9 specific ? There is mismatch between comment and macro name > + > +/** > + * 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. > + * > + * 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. > */ > -#define GUC_WOPCM_TOP (512 << 10) > -#define BXT_GUC_WOPCM_RC6_RESERVED (64 << 10) > +struct intel_guc_wopcm { > + u32 offset; > + u32 size; > + u32 top; > + u32 valid; > +}; > -u32 intel_guc_wopcm_size(struct intel_guc *guc); > +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); > #endif > diff --git a/drivers/gpu/drm/i915/intel_huc.c > b/drivers/gpu/drm/i915/intel_huc.c > index aed9c1c..dc6a6c6 100644 > --- a/drivers/gpu/drm/i915/intel_huc.c > +++ b/drivers/gpu/drm/i915/intel_huc.c > @@ -206,7 +206,7 @@ int intel_huc_auth(struct intel_huc *huc) > return -ENOEXEC; > vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0, > - PIN_OFFSET_BIAS | GUC_WOPCM_TOP); > + PIN_OFFSET_BIAS | guc->wopcm.top); > if (IS_ERR(vma)) { > ret = PTR_ERR(vma); > DRM_ERROR("HuC: Failed to pin huc fw object %d\n", ret); > diff --git a/drivers/gpu/drm/i915/intel_uc.c > b/drivers/gpu/drm/i915/intel_uc.c > index 1b2831b..c842f36 100644 > --- a/drivers/gpu/drm/i915/intel_uc.c > +++ b/drivers/gpu/drm/i915/intel_uc.c > @@ -283,6 +283,9 @@ void intel_uc_fini_misc(struct drm_i915_private > *dev_priv) > int intel_uc_init(struct drm_i915_private *dev_priv) > { > struct intel_guc *guc = &dev_priv->guc; > + struct intel_huc *huc = &dev_priv->huc; > + u32 guc_fw_size = intel_uc_fw_get_size(&guc->fw); > + u32 huc_fw_size = intel_uc_fw_get_size(&huc->fw); > int ret; > if (!USES_GUC(dev_priv)) > @@ -291,6 +294,10 @@ int intel_uc_init(struct drm_i915_private *dev_priv) > if (!HAS_GUC(dev_priv)) > return -ENODEV; > + ret = intel_guc_wopcm_init(&guc->wopcm, guc_fw_size, huc_fw_size); > + if (ret) > + return ret; > + > ret = intel_guc_init(guc); > if (ret) > return ret; > @@ -340,9 +347,9 @@ int intel_uc_init_hw(struct drm_i915_private > *dev_priv) > gen9_reset_guc_interrupts(dev_priv); > /* init WOPCM */ > - I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(guc)); > + I915_WRITE(GUC_WOPCM_SIZE, guc->wopcm.size); > I915_WRITE(DMA_GUC_WOPCM_OFFSET, > - GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC); > + guc->wopcm.offset | HUC_LOADING_AGENT_GUC); > /* WaEnableuKernelHeaderValidFix:skl */ > /* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */ > diff --git a/drivers/gpu/drm/i915/intel_uc_fw.c > b/drivers/gpu/drm/i915/intel_uc_fw.c > index 24945cf..791263a 100644 > --- a/drivers/gpu/drm/i915/intel_uc_fw.c > +++ b/drivers/gpu/drm/i915/intel_uc_fw.c > @@ -95,9 +95,13 @@ void intel_uc_fw_fetch(struct drm_i915_private > *dev_priv, > uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size; > uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); > - /* Header and uCode will be loaded to WOPCM */ > + /* > + * Header and uCode will be loaded to WOPCM > + * Only check the size against the overall available WOPCM here. Will > + * continue to check the size during WOPCM partition calculation. > + */ Hmm, maybe we can drop completely this early check from here, as we have this extra check later ? > size = uc_fw->header_size + uc_fw->ucode_size; > - if (size > intel_guc_wopcm_size(&dev_priv->guc)) { > + if (size > WOPCM_DEFAULT_SIZE) { > DRM_WARN("%s: Firmware is too large to fit in WOPCM\n", > intel_uc_fw_type_repr(uc_fw->type)); > err = -E2BIG; > @@ -207,6 +211,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, > int (*xfer)(struct intel_uc_fw *uc_fw, > struct i915_vma *vma)) > { > + struct drm_i915_private *i915 = to_i915(uc_fw->obj->base.dev); > struct i915_vma *vma; > int err; > @@ -230,7 +235,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, > } > vma = i915_gem_object_ggtt_pin(uc_fw->obj, NULL, 0, 0, > - PIN_OFFSET_BIAS | GUC_WOPCM_TOP); > + PIN_OFFSET_BIAS | i915->guc.wopcm.top); > if (IS_ERR(vma)) { > err = PTR_ERR(vma); > DRM_DEBUG_DRIVER("%s fw ggtt-pin err=%d\n", > diff --git a/drivers/gpu/drm/i915/intel_uc_fw.h > b/drivers/gpu/drm/i915/intel_uc_fw.h > index d5fd460..ed3043d 100644 > --- a/drivers/gpu/drm/i915/intel_uc_fw.h > +++ b/drivers/gpu/drm/i915/intel_uc_fw.h > @@ -115,6 +115,22 @@ static inline bool intel_uc_fw_is_selected(struct > intel_uc_fw *uc_fw) > return uc_fw->path != NULL; > } > +/** > + * intel_uc_fw_get_size() - Get the size of the firmware. > + * @uc_fw: intel_uc_fw structure. > + * > + * Get the size of the firmware that will be placed in WOPCM. > + * > + * Return: Size of the firmware, or zero on firmware fetch failure. > + */ > +static inline u32 intel_uc_fw_get_size(struct intel_uc_fw *uc_fw) > +{ > + if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS) > + return 0; > + > + return uc_fw->header_size + uc_fw->ucode_size; > +} > + > void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, > struct intel_uc_fw *uc_fw); > int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
On 02/13/2018 07:56 AM, Michal Wajdeczko wrote: >> + >> +/* GUC WOPCM offset needs to be 16KB aligned. */ >> +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) >> +/* 16KB reserved at the beginning of GuC WOPCM. */ >> +#define GUC_WOPCM_RESERVED (16 << 10) >> +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ >> +#define GUC_WOPCM_STACK_RESERVED (8 << 10) >> +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ >> +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) > > Hmm, I'm still not convinced that we correctly attach it to "GuC WOPCM". Can you explain the reason and more about your concerns? > > In the result, maybe we should stop focusing on "intel_guc_wopcm" and > define > everything as "intel_wopcm" as it was earlier suggested by Joonas. > > Then we can define our struct to match diagram above as > > struct intel_wopcm { > u32 size; > struct { > u32 base; > u32 size; > } guc; > }; > Thanks Michal! but I don't think this is quite clean design. two reason: 0) I agree there should be something like intel_wopcm to describe the overall wopcm info, that what I did in my v1 patch series. the question is whether we really need it even if we are using only static wopcm size value? I think it should be more clear to introduce intel_wopcm as a part of a patch that supports dynamic wopcm size calculation. 2) the wopcm region (a.k.a partition) is definitely a concept that should exist at least in uc layer. if we chose not to init uc/guc, it would be nonsense to init these partitions/info in an intel_wopcm which attached to drm_i915_private and we have initialized a part of this struct (e.g. size). to make it clean I will insist to have the guc wopcm region (or maybe the other region info) kept in uc level. As I said the purpose of this series is to enable dynamic GuC WOPCM offset and size calculation. it's not targeting any code refactoring and only serves as a new feature enabling patch. The design principle of this series was to provide clear new feature enabling by: 1) align with current code design and implementation. 2) provide correct hardware abstraction. 3) faultlessly enabled these new features. (e.g. dynamic size/offset calculation) I think this series is now in a good shape to meet all above targets. On the other hand, I do agree there always is some room to make our current code clearer, but what we are talking about is further code refactoring. Actually, I've already had some ideas of this. e.g. we could have some new abstractions such as: struct intel_wopcm { u32 size; /*any other global wopcm info*/ } struct wopcm_region { u32 reserved; // reserved size in each region u32 offset; // offset of each region u32 size; // size of each region }; struct intel_uc { struct wopcm_regions regions[]; struct intel_uc_fw fws[]; struct intel_guc guc; ... } struct intel_guc_dma { u32 fw_domains; lockable_reg size; lockable_reg offset; u32 flags; // e.g. loading HuC. } guc_dma_init() guc_dma_hw_init() guc_dma_xfer() struct intel_guc { struct intel_guc_dma guc_dma; /*other guc objects*/ } This would provide better software layer abstraction. but what I learned from the 10 versions code submission is we need make things clear enough to move forward. The lack of uc level abstraction is probably the reason why we felt so frustrating about this part of code. Can we just move forward by aligning to the current code implementation? since what we need now is enable this new features. and we definitely can provide more code refactoring patches based on these changes later. Regards, -Jackie
On Tue, 13 Feb 2018 21:01:04 +0100, Yaodong Li <yaodong.li@intel.com> wrote: > On 02/13/2018 07:56 AM, Michal Wajdeczko wrote: >>> + >>> +/* GUC WOPCM offset needs to be 16KB aligned. */ >>> +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) >>> +/* 16KB reserved at the beginning of GuC WOPCM. */ >>> +#define GUC_WOPCM_RESERVED (16 << 10) >>> +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ >>> +#define GUC_WOPCM_STACK_RESERVED (8 << 10) >>> +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ >>> +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) >> >> Hmm, I'm still not convinced that we correctly attach it to "GuC WOPCM". > Can you explain the reason and more about your concerns? Spec says that CTX reserved region is separate from GuC region and is calculated "against the address of the top of WOPCM". >> >> In the result, maybe we should stop focusing on "intel_guc_wopcm" and >> define >> everything as "intel_wopcm" as it was earlier suggested by Joonas. >> >> Then we can define our struct to match diagram above as >> >> struct intel_wopcm { >> u32 size; >> struct { >> u32 base; >> u32 size; >> } guc; >> }; >> > Thanks Michal! but I don't think this is quite clean design. two reason: > 0) I agree there should be something like intel_wopcm to describe > the overall wopcm info, that what I did in my v1 patch series. > the question is whether we really need it even if we are using > only static wopcm size value? Are you sure that WOPCM size is same for all platforms ? > I think it should be more clear to > introduce intel_wopcm as a part of a patch that supports dynamic > wopcm size calculation. This is exactly what I'm suggesting. Note that spec uses "arbiter" term, and we can use struct intel_wopcm as placeholder for the partitioning results. > 2) the wopcm region (a.k.a partition) is definitely a concept that should > exist at least in uc layer. I'm not so sure. But definitely it should not be guc only concept. > if we chose not to init uc/guc, it would be > nonsense to init these partitions/info in an intel_wopcm which > attached to > drm_i915_private and we have initialized a part of this struct > (e.g. size). Why nonsense? Since WOPCM is used/needed by several entities (agents) then it is obvious that to properly partition wopcm into regions that will satisfy all agents, arbiter need to know their specific requirements (like fw sizes in case of HuC or GuC). > to make it clean I will insist to have the guc wopcm region (or > maybe > the other region info) kept in uc level. It will not be clean if you make calculation in one place and keep partial results in other places. We can at most setup hw from uc code. > As I said the purpose of this series is to enable dynamic GuC WOPCM > offset > and size calculation. Sure. but and all I want is to do it in a right place. > it's not targeting any code refactoring and only serves > as a new feature enabling patch. The design principle of this series was > to > provide clear new feature enabling by: > 1) align with current code design and implementation. > 2) provide correct hardware abstraction. > 3) faultlessly enabled these new features. (e.g. dynamic size/offset > calculation) > I think this series is now in a good shape to meet all above targets. > So we need arbiter :) > On the other hand, I do agree there always is some room to make our > current > code clearer, but what we are talking about is further code refactoring. > Actually, I've already had some ideas of this. e.g. we could have some > new > abstractions such as: > > struct intel_wopcm { > u32 size; > /*any other global wopcm info*/ > } > > struct wopcm_region { > u32 reserved; // reserved size in each region > u32 offset; // offset of each region > u32 size; // size of each region > }; > > struct intel_uc { > struct wopcm_regions regions[]; > struct intel_uc_fw fws[]; > struct intel_guc guc; > ... > } > > struct intel_guc_dma { > u32 fw_domains; > lockable_reg size; > lockable_reg offset; > u32 flags; // e.g. loading HuC. > } > > guc_dma_init() > guc_dma_hw_init() > guc_dma_xfer() > > struct intel_guc { > struct intel_guc_dma guc_dma; > /*other guc objects*/ > } > > This would provide better software layer abstraction. but what I learned > from the 10 versions code submission is we need make things clear enough > to > move forward. The lack of uc level abstraction is probably the reason > why we > felt so frustrating about this part of code. Yep. struct intel_uc will likely make few things cleaner. > > Can we just move forward by aligning to the current code implementation? > since what we need now is enable this new features. and we definitely can > provide more code refactoring patches based on these changes later. This is question to the maintainers. > > Regards, > -Jackie
On 02/13/2018 07:56 AM, Michal Wajdeczko wrote: > > Also, comparing again your drawing with the spec I think it > should look like this: > > +-------- +====================+ <== WOPCM top > /|\ | HW RESERVED | > | +------------------- + > | | | > | +-- +====================+ <== GuC WOPCM top > | /|\ | | > | | | | > | GuC | | > | WOPCM | GuC firmware | > | size | | > WOPCM | +--------------------+ > size \|/ | GuC reserved | > | +-- +====================+ <== GuC WOPCM base (offset from > WOPCM base) > | | WOPCM RESERVED | > | +--------------------+ > \|/ | HuC firmware | > +-------- +====================+ <== WOPCM base > > >> + >> +/* GUC WOPCM offset needs to be 16KB aligned. */ >> +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) >> +/* 16KB reserved at the beginning of GuC WOPCM. */ >> +#define GUC_WOPCM_RESERVED (16 << 10) >> +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ >> +#define GUC_WOPCM_STACK_RESERVED (8 << 10) >> +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ >> +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) > > Hmm, I'm still not convinced that we correctly attach it to "GuC WOPCM". > > In the result, maybe we should stop focusing on "intel_guc_wopcm" and > define > everything as "intel_wopcm" as it was earlier suggested by Joonas. > > Then we can define our struct to match diagram above as > > struct intel_wopcm { > u32 size; > struct { > u32 base; > u32 size; > } guc; > }; > > u32 intel_wopcm_guc_top(struct intel_wopcm *wopcm) > { > return wopcm->guc.base + wopcm->guc.size; > } More comments about the *top*:-) The *top* we used in driver code to verify where the non-wopcm guc memory allocation starts is the offset value from guc.base to WOPCM_TOP. so we don't need to add the guc.base to it. From GuC point of view the *top* (boundary between wopcm and non-wopcm memory) is like: +------------------+ <=== GuC memory end Non WOPCM +------------------+ <=== *top* (This is actually in top of the whole WOPCM) CTX Rsvd +------------------+ <=== *__top* (wopcm->guc.size) WOPCM +------------------+ <=== guc base (wopcm->guc.base) actually, we had a discussion whether we should set the guc wopcm and non-wopcm boundary to *__top* but the suggestion we got is to stick to the spec description which says to allocate "non-wopcm memory starts from WOPCM TOP"(even if it seems we could use *__top* as the boundary:-)). and it's because of this reason we called the wopcm from guc_base to *top* as GuC WOPCM since we count the wopcm between *__top* to *top* as a part of GuC address space while trying to allocate non-wopcm for GuC (even though GuC won't access CTX Rsvd at allO:-)). Regards, -Jackie
On Wed, 14 Feb 2018 03:26:12 +0100, Yaodong Li <yaodong.li@intel.com> wrote: > > On 02/13/2018 07:56 AM, Michal Wajdeczko wrote: >> >> Also, comparing again your drawing with the spec I think it >> should look like this: >> >> +-------- +====================+ <== WOPCM top >> /|\ | HW RESERVED | >> | +------------------- + >> | | | >> | +-- +====================+ <== GuC WOPCM top >> | /|\ | | >> | | | | >> | GuC | | >> | WOPCM | GuC firmware | >> | size | | >> WOPCM | +--------------------+ >> size \|/ | GuC reserved | >> | +-- +====================+ <== GuC WOPCM base (offset from >> WOPCM base) >> | | WOPCM RESERVED | >> | +--------------------+ >> \|/ | HuC firmware | >> +-------- +====================+ <== WOPCM base >> >> >>> + >>> +/* GUC WOPCM offset needs to be 16KB aligned. */ >>> +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) >>> +/* 16KB reserved at the beginning of GuC WOPCM. */ >>> +#define GUC_WOPCM_RESERVED (16 << 10) >>> +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ >>> +#define GUC_WOPCM_STACK_RESERVED (8 << 10) >>> +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ >>> +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) >> >> Hmm, I'm still not convinced that we correctly attach it to "GuC WOPCM". >> >> In the result, maybe we should stop focusing on "intel_guc_wopcm" and >> define >> everything as "intel_wopcm" as it was earlier suggested by Joonas. >> >> Then we can define our struct to match diagram above as >> >> struct intel_wopcm { >> u32 size; >> struct { >> u32 base; >> u32 size; >> } guc; >> }; >> >> u32 intel_wopcm_guc_top(struct intel_wopcm *wopcm) >> { >> return wopcm->guc.base + wopcm->guc.size; >> } > More comments about the *top*:-) > > The *top* we used in driver code to verify where the non-wopcm guc memory > allocation starts is the offset value from guc.base to WOPCM_TOP. so we > don't need to > add the guc.base to it. > > From GuC point of view the *top* (boundary between wopcm and non-wopcm > memory) > is like: > +------------------+ <=== GuC memory end > > Non WOPCM > > +------------------+ <=== *top* (This is actually in top of the whole > WOPCM) > CTX Rsvd > +------------------+ <=== *__top* (wopcm->guc.size) > WOPCM > +------------------+ <=== guc base (wopcm->guc.base) > > actually, we had a discussion whether we should set the guc wopcm and > non-wopcm > boundary to *__top* but the suggestion we got is to stick to the spec > description which > says to allocate "non-wopcm memory starts from WOPCM TOP"(even if it > seems > we could use *__top* as the boundary:-)). and it's because of this > reason we called the > wopcm from guc_base to *top* as GuC WOPCM since we count the wopcm > between > *__top* to *top* as a part of GuC address space while trying to allocate > non-wopcm > for GuC (even though GuC won't access CTX Rsvd at allO:-)). Ok, so we should program GUC_WOPCM_SIZE to wopcm->guc.size and then use (wopcm->size - wopcm->guc.base) as offset to i915_vma_pin, right? Michal
On 02/14/2018 09:38 AM, Michal Wajdeczko wrote: > On Wed, 14 Feb 2018 03:26:12 +0100, Yaodong Li <yaodong.li@intel.com> > wrote: > >> >> On 02/13/2018 07:56 AM, Michal Wajdeczko wrote: >>> >>> Also, comparing again your drawing with the spec I think it >>> should look like this: >>> >>> +-------- +====================+ <== WOPCM top >>> /|\ | HW RESERVED | >>> | +------------------- + >>> | | | >>> | +-- +====================+ <== GuC WOPCM top >>> | /|\ | | >>> | | | | >>> | GuC | | >>> | WOPCM | GuC firmware | >>> | size | | >>> WOPCM | +--------------------+ >>> size \|/ | GuC reserved | >>> | +-- +====================+ <== GuC WOPCM base (offset from >>> WOPCM base) >>> | | WOPCM RESERVED | >>> | +--------------------+ >>> \|/ | HuC firmware | >>> +-------- +====================+ <== WOPCM base >>> >>> >>>> + >>>> +/* GUC WOPCM offset needs to be 16KB aligned. */ >>>> +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) >>>> +/* 16KB reserved at the beginning of GuC WOPCM. */ >>>> +#define GUC_WOPCM_RESERVED (16 << 10) >>>> +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ >>>> +#define GUC_WOPCM_STACK_RESERVED (8 << 10) >>>> +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ >>>> +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) >>> >>> Hmm, I'm still not convinced that we correctly attach it to "GuC >>> WOPCM". >>> >>> In the result, maybe we should stop focusing on "intel_guc_wopcm" >>> and define >>> everything as "intel_wopcm" as it was earlier suggested by Joonas. >>> >>> Then we can define our struct to match diagram above as >>> >>> struct intel_wopcm { >>> u32 size; >>> struct { >>> u32 base; >>> u32 size; >>> } guc; >>> }; >>> >>> u32 intel_wopcm_guc_top(struct intel_wopcm *wopcm) >>> { >>> return wopcm->guc.base + wopcm->guc.size; >>> } >> More comments about the *top*:-) >> >> The *top* we used in driver code to verify where the non-wopcm guc >> memory >> allocation starts is the offset value from guc.base to WOPCM_TOP. so >> we don't need to >> add the guc.base to it. >> >> From GuC point of view the *top* (boundary between wopcm and >> non-wopcm memory) >> is like: >> +------------------+ <=== GuC memory end >> >> Non WOPCM >> >> +------------------+ <=== *top* (This is actually in top of the whole >> WOPCM) >> CTX Rsvd >> +------------------+ <=== *__top* (wopcm->guc.size) >> WOPCM >> +------------------+ <=== guc base (wopcm->guc.base) >> >> actually, we had a discussion whether we should set the guc wopcm and >> non-wopcm >> boundary to *__top* but the suggestion we got is to stick to the spec >> description which >> says to allocate "non-wopcm memory starts from WOPCM TOP"(even if it >> seems >> we could use *__top* as the boundary:-)). and it's because of this >> reason we called the >> wopcm from guc_base to *top* as GuC WOPCM since we count the wopcm >> between >> *__top* to *top* as a part of GuC address space while trying to >> allocate non-wopcm >> for GuC (even though GuC won't access CTX Rsvd at allO:-)). > > Ok, so we should program GUC_WOPCM_SIZE to wopcm->guc.size and then > use (wopcm->size - wopcm->guc.base) as offset to i915_vma_pin, right? > All values of size and top are relevant to guc.base (offsets from guc.base), and currently we are using guc.size + hole between guc.size and ctx reserved (if any) + ctx reserved size as the offset to i915_vma_pin as it's the actual WOPCM_TOP.:-) Regards, -Jackie
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 3d75f48..414eab2 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -312,12 +312,13 @@ __create_hw_context(struct drm_i915_private *dev_priv, ctx->desc_template = default_desc_template(dev_priv, dev_priv->mm.aliasing_ppgtt); - /* GuC requires the ring to be placed above GUC_WOPCM_TOP. If GuC is not + /* + * GuC requires the ring to be placed above GuC WOPCM top. If GuC is not * present or not in use we still need a small bias as ring wraparound * at offset 0 sometimes hangs. No idea why. */ if (USES_GUC(dev_priv)) - ctx->ggtt_offset_bias = GUC_WOPCM_TOP; + ctx->ggtt_offset_bias = dev_priv->guc.wopcm.top; else ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE; diff --git a/drivers/gpu/drm/i915/intel_guc.c b/drivers/gpu/drm/i915/intel_guc.c index fdf8cb3..578e9f4 100644 --- a/drivers/gpu/drm/i915/intel_guc.c +++ b/drivers/gpu/drm/i915/intel_guc.c @@ -65,6 +65,7 @@ void intel_guc_init_early(struct intel_guc *guc) intel_guc_fw_init_early(guc); intel_guc_ct_init_early(&guc->ct); intel_guc_log_init_early(guc); + intel_guc_wopcm_init_early(&guc->wopcm); mutex_init(&guc->send_mutex); guc->send = intel_guc_send_nop; @@ -477,7 +478,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv) * This is a wrapper to create an object for use with the GuC. In order to * use it inside the GuC, an object needs to be pinned lifetime, so we allocate * both some backing storage and a range inside the Global GTT. We must pin - * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because that + * it in the GGTT somewhere other than than [0, GuC WOPCM top) because that * range is reserved inside GuC. * * Return: A i915_vma if successful, otherwise an ERR_PTR. @@ -498,7 +499,7 @@ struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size) goto err; ret = i915_vma_pin(vma, 0, PAGE_SIZE, - PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + PIN_GLOBAL | PIN_OFFSET_BIAS | guc->wopcm.top); if (ret) { vma = ERR_PTR(ret); goto err; diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 50be6de..06f315e 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -49,6 +49,7 @@ struct intel_guc { struct intel_uc_fw fw; struct intel_guc_log log; struct intel_guc_ct ct; + struct intel_guc_wopcm wopcm; /* Log snapshot if GuC errors during load */ struct drm_i915_gem_object *load_err_log; @@ -109,10 +110,10 @@ static inline void intel_guc_notify(struct intel_guc *guc) * @guc: intel guc. * @vma: i915 graphics virtual memory area. * - * GuC does not allow any gfx GGTT address that falls into range [0, WOPCM_TOP), - * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top address is - * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects - * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM. + * GuC does not allow any gfx GGTT address that falls into range + * [0, GuC WOPCM top), which is reserved for Boot ROM, SRAM and WOPCM. + * All gfx objects used by GuC is pinned with PIN_OFFSET_BIAS along with + * top of WOPCM. * * Return: GGTT offset that meets the GuC gfx address requirement. */ @@ -121,7 +122,8 @@ static inline u32 intel_guc_ggtt_offset(struct intel_guc *guc, { u32 offset = i915_ggtt_offset(vma); - GEM_BUG_ON(offset < GUC_WOPCM_TOP); + GEM_BUG_ON(!guc->wopcm.valid); + GEM_BUG_ON(offset < guc->wopcm.top); GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP)); return offset; diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h index de4f78b..c482df5 100644 --- a/drivers/gpu/drm/i915/intel_guc_reg.h +++ b/drivers/gpu/drm/i915/intel_guc_reg.h @@ -75,6 +75,8 @@ /* Defines WOPCM space available to GuC firmware */ #define GUC_WOPCM_SIZE _MMIO(0xc050) +#define GUC_WOPCM_SIZE_SHIFT 12 +#define GUC_WOPCM_SIZE_MASK (0xfffff << GUC_WOPCM_SIZE_SHIFT) #define GEN8_GT_PM_CONFIG _MMIO(0x138140) #define GEN9LP_GT_PM_CONFIG _MMIO(0x138140) diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.c b/drivers/gpu/drm/i915/intel_guc_wopcm.c index 3972901..cf832b9 100644 --- a/drivers/gpu/drm/i915/intel_guc_wopcm.c +++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c @@ -7,22 +7,128 @@ #include "intel_guc_wopcm.h" #include "i915_drv.h" +static inline u32 context_reserved_size(struct intel_guc *guc) +{ + struct drm_i915_private *i915 = guc_to_i915(guc); + + if (IS_GEN9_LP(i915)) + return BXT_GUC_WOPCM_RC6_CTX_RESERVED; + + return 0; +} + +static inline int gen9_check_dword_gap(struct intel_guc_wopcm *guc_wopcm) +{ + u32 guc_wopcm_start; + u32 delta; + + /* + * GuC WOPCM size is at least a dword larger than the offset from WOPCM + * base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET) due + * to hardware limitation on Gen9. + */ + guc_wopcm_start = guc_wopcm->offset + GEN9_GUC_WOPCM_OFFSET; + if (guc_wopcm_start > guc_wopcm->size) + return -E2BIG; + + delta = guc_wopcm->size - guc_wopcm_start; + if (delta < sizeof(u32)) + return -E2BIG; + + return 0; +} + +static inline int guc_wopcm_size_check(struct intel_guc *guc) +{ + struct drm_i915_private *i915 = guc_to_i915(guc); + struct intel_guc_wopcm *guc_wopcm = &guc->wopcm; + + if (IS_GEN9(i915)) + return gen9_check_dword_gap(guc_wopcm); + + return 0; +} + /** - * intel_guc_wopcm_size() - Get the size of GuC WOPCM. - * @guc: intel guc. + * intel_guc_wopcm_init_early() - Early initialization of the GuC WOPCM. + * @guc_wopcm: pointer to intel_guc_wopcm. * - * Get the platform specific GuC WOPCM size. + * Setup the GuC WOPCM top to the top of the overall WOPCM. This will guarantee + * that the allocation of the GuC accessible objects won't fall into WOPCM when + * GuC partition isn't present. * - * Return: size of the GuC WOPCM. */ -u32 intel_guc_wopcm_size(struct intel_guc *guc) +void intel_guc_wopcm_init_early(struct intel_guc_wopcm *guc_wopcm) { - struct drm_i915_private *i915 = guc_to_i915(guc); - u32 size = GUC_WOPCM_TOP; + guc_wopcm->top = WOPCM_DEFAULT_SIZE; +} - /* On BXT, the top of WOPCM is reserved for RC6 context */ - if (IS_GEN9_LP(i915)) - size -= BXT_GUC_WOPCM_RC6_RESERVED; +/** + * intel_guc_wopcm_init() - Initialize the GuC WOPCM. + * @guc_wopcm: pointer to intel_guc_wopcm. + * @guc_fw_size: size of GuC firmware. + * @huc_fw_size: size of HuC firmware. + * + * Calculate the GuC WOPCM offset and size based on GuC and HuC firmware sizes. + * This function will set the GuC WOPCM size to the size of maximum WOPCM + * available for GuC. This function will also enforce platform dependent + * hardware restrictions on GuC WOPCM offset and size. It will fail the GuC + * WOPCM init if any of these checks were failed, so that the following GuC + * firmware uploading would be aborted. + * + * Return: 0 on success, non-zero error code on failure. + */ +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); + u32 reserved = context_reserved_size(guc); + u32 offset, size, top; + int err; + + GEM_BUG_ON(!guc_fw_size); + + offset = huc_fw_size + WOPCM_RESERVED_SIZE; + + /* Hardware requires GuC WOPCM offset to be 16K aligned. */ + offset = ALIGN(offset, GUC_WOPCM_OFFSET_ALIGNMENT); + if ((offset + reserved) >= WOPCM_DEFAULT_SIZE) { + DRM_DEBUG_DRIVER("GuC WOPCM offset exceeds max WOPCM size.\n"); + return -E2BIG; + } + + top = WOPCM_DEFAULT_SIZE - offset; + size = top - reserved; + + /* GuC WOPCM size must be multiple of 4K pages */ + size &= GUC_WOPCM_SIZE_MASK; + + /* + * GuC firmware size needs to be less than or equal to the size of the + * available GuC WOPCM (total available GuC WOPCM size - reserved size). + * Need extra 8K stack for GuC firmware. + */ + reserved = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; + if ((guc_fw_size + reserved) > size) { + DRM_DEBUG_DRIVER("No enough GuC WOPCM for GuC firmware.\n"); + return -E2BIG; + } + + guc->wopcm.offset = offset; + guc->wopcm.size = size; + guc->wopcm.top = top; + + err = guc_wopcm_size_check(guc); + if (err) { + DRM_DEBUG_DRIVER("GuC WOPCM size check failed.\n"); + return err; + } + + guc->wopcm.valid = 1; + + DRM_DEBUG_DRIVER("GuC WOPCM offset %dKB, size %dKB, top %dKB\n", + offset >> 10, size >> 10, top >> 10); - return size; + return 0; } diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h b/drivers/gpu/drm/i915/intel_guc_wopcm.h index 8c4f693..a632caa 100644 --- a/drivers/gpu/drm/i915/intel_guc_wopcm.h +++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h @@ -11,15 +11,73 @@ struct intel_guc; -/* 512KB static offset from WOPCM base. */ -#define GUC_WOPCM_OFFSET_VALUE (512 << 10) /* - * 512KB static GuC WOPCM size from GUC_WOPCM_OFFSET_VALUE to the end of GuC - * WOPCM. GuC addresses below GUC_WOPCM_TOP don't map through the GTT. + * The layout of the WOPCM will be determined by GuC WOPCM size and offset + * registers settings. Currently, GuC WOPCM code calculates the GuC WOPCM offset + * and size values based on a layout as shown below: + * + * +=====+==>+====================+<== GuC WOPCM top + * ^ ^ | HW contexts RSVD | + * | | +--------------------+<== GuC WOPCM size + * | | | | + * | GuC | | + * | WOPCM +--------------------+<== (Platform specific size) + * | | | GuC FW RSVD | + * WOPCM | +------------------- +<== (16KB) + * | v | RSVD GuC WOPCM | + * | +==>+====================+<== GuC WOPCM offset + * | | RSVD WOPCM | + * | +------------------- + + * v | HuC FW | + * +========>+====================+<== WOPCM Base + * + * GuC accessible WOPCM starts at GuC WOPCM offset and ends at GuC WOPCM size. + * The top part of the GuC WOPCM is reserved for hardware contexts (e.g. RC6 + * context). We need to keep tracking the GuC WOPCM top since hardware requires + * the GGTT offset of a GuC accessible GEM buffer to be larger than the value of + * GuC WOPCM top. The values of GuC WOPCM size and top should be set to the + * length from GuC WOPCM offset in bytes. + */ + +/* Default WOPCM size 1MB. */ +#define WOPCM_DEFAULT_SIZE (1 << 20) +/* The initial 16KB WOPCM (RSVD WOPCM) is reserved. */ +#define WOPCM_RESERVED_SIZE (16 << 10) + +/* GUC WOPCM offset needs to be 16KB aligned. */ +#define GUC_WOPCM_OFFSET_ALIGNMENT (16 << 10) +/* 16KB reserved at the beginning of GuC WOPCM. */ +#define GUC_WOPCM_RESERVED (16 << 10) +/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ +#define GUC_WOPCM_STACK_RESERVED (8 << 10) +/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */ +#define BXT_GUC_WOPCM_RC6_CTX_RESERVED (24 << 10) + +/* + * GuC WOPCM starts at 144KB (GUC_WOPCM_RESERVED + 128KB reserved for GuC + * firmware loading) from GuC WOPCM offset on BXT. + */ +#define GEN9_GUC_WOPCM_OFFSET (144 << 10) + +/** + * 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. + * + * 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. */ -#define GUC_WOPCM_TOP (512 << 10) -#define BXT_GUC_WOPCM_RC6_RESERVED (64 << 10) +struct intel_guc_wopcm { + u32 offset; + u32 size; + u32 top; + u32 valid; +}; -u32 intel_guc_wopcm_size(struct intel_guc *guc); +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); #endif diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c index aed9c1c..dc6a6c6 100644 --- a/drivers/gpu/drm/i915/intel_huc.c +++ b/drivers/gpu/drm/i915/intel_huc.c @@ -206,7 +206,7 @@ int intel_huc_auth(struct intel_huc *huc) return -ENOEXEC; vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0, - PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + PIN_OFFSET_BIAS | guc->wopcm.top); if (IS_ERR(vma)) { ret = PTR_ERR(vma); DRM_ERROR("HuC: Failed to pin huc fw object %d\n", ret); diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 1b2831b..c842f36 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -283,6 +283,9 @@ void intel_uc_fini_misc(struct drm_i915_private *dev_priv) int intel_uc_init(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; + struct intel_huc *huc = &dev_priv->huc; + u32 guc_fw_size = intel_uc_fw_get_size(&guc->fw); + u32 huc_fw_size = intel_uc_fw_get_size(&huc->fw); int ret; if (!USES_GUC(dev_priv)) @@ -291,6 +294,10 @@ int intel_uc_init(struct drm_i915_private *dev_priv) if (!HAS_GUC(dev_priv)) return -ENODEV; + ret = intel_guc_wopcm_init(&guc->wopcm, guc_fw_size, huc_fw_size); + if (ret) + return ret; + ret = intel_guc_init(guc); if (ret) return ret; @@ -340,9 +347,9 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) gen9_reset_guc_interrupts(dev_priv); /* init WOPCM */ - I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(guc)); + I915_WRITE(GUC_WOPCM_SIZE, guc->wopcm.size); I915_WRITE(DMA_GUC_WOPCM_OFFSET, - GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC); + guc->wopcm.offset | HUC_LOADING_AGENT_GUC); /* WaEnableuKernelHeaderValidFix:skl */ /* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */ diff --git a/drivers/gpu/drm/i915/intel_uc_fw.c b/drivers/gpu/drm/i915/intel_uc_fw.c index 24945cf..791263a 100644 --- a/drivers/gpu/drm/i915/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/intel_uc_fw.c @@ -95,9 +95,13 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size; uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); - /* Header and uCode will be loaded to WOPCM */ + /* + * Header and uCode will be loaded to WOPCM + * Only check the size against the overall available WOPCM here. Will + * continue to check the size during WOPCM partition calculation. + */ size = uc_fw->header_size + uc_fw->ucode_size; - if (size > intel_guc_wopcm_size(&dev_priv->guc)) { + if (size > WOPCM_DEFAULT_SIZE) { DRM_WARN("%s: Firmware is too large to fit in WOPCM\n", intel_uc_fw_type_repr(uc_fw->type)); err = -E2BIG; @@ -207,6 +211,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, int (*xfer)(struct intel_uc_fw *uc_fw, struct i915_vma *vma)) { + struct drm_i915_private *i915 = to_i915(uc_fw->obj->base.dev); struct i915_vma *vma; int err; @@ -230,7 +235,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, } vma = i915_gem_object_ggtt_pin(uc_fw->obj, NULL, 0, 0, - PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + PIN_OFFSET_BIAS | i915->guc.wopcm.top); if (IS_ERR(vma)) { err = PTR_ERR(vma); DRM_DEBUG_DRIVER("%s fw ggtt-pin err=%d\n", diff --git a/drivers/gpu/drm/i915/intel_uc_fw.h b/drivers/gpu/drm/i915/intel_uc_fw.h index d5fd460..ed3043d 100644 --- a/drivers/gpu/drm/i915/intel_uc_fw.h +++ b/drivers/gpu/drm/i915/intel_uc_fw.h @@ -115,6 +115,22 @@ static inline bool intel_uc_fw_is_selected(struct intel_uc_fw *uc_fw) return uc_fw->path != NULL; } +/** + * intel_uc_fw_get_size() - Get the size of the firmware. + * @uc_fw: intel_uc_fw structure. + * + * Get the size of the firmware that will be placed in WOPCM. + * + * Return: Size of the firmware, or zero on firmware fetch failure. + */ +static inline u32 intel_uc_fw_get_size(struct intel_uc_fw *uc_fw) +{ + if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS) + return 0; + + return uc_fw->header_size + uc_fw->ucode_size; +} + void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, struct intel_uc_fw *uc_fw); int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,