Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: MIT
0002 /*
0003  * Copyright © 2017-2019 Intel Corporation
0004  */
0005 
0006 #include "intel_wopcm.h"
0007 #include "i915_drv.h"
0008 
0009 /**
0010  * DOC: WOPCM Layout
0011  *
0012  * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
0013  * offset registers whose values are calculated and determined by HuC/GuC
0014  * firmware size and set of hardware requirements/restrictions as shown below:
0015  *
0016  * ::
0017  *
0018  *    +=========> +====================+ <== WOPCM Top
0019  *    ^           |  HW contexts RSVD  |
0020  *    |     +===> +====================+ <== GuC WOPCM Top
0021  *    |     ^     |                    |
0022  *    |     |     |                    |
0023  *    |     |     |                    |
0024  *    |    GuC    |                    |
0025  *    |   WOPCM   |                    |
0026  *    |    Size   +--------------------+
0027  *  WOPCM   |     |    GuC FW RSVD     |
0028  *    |     |     +--------------------+
0029  *    |     |     |   GuC Stack RSVD   |
0030  *    |     |     +------------------- +
0031  *    |     v     |   GuC WOPCM RSVD   |
0032  *    |     +===> +====================+ <== GuC WOPCM base
0033  *    |           |     WOPCM RSVD     |
0034  *    |           +------------------- + <== HuC Firmware Top
0035  *    v           |      HuC FW        |
0036  *    +=========> +====================+ <== WOPCM Base
0037  *
0038  * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
0039  * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
0040  * context).
0041  */
0042 
0043 /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
0044 #define GEN11_WOPCM_SIZE        SZ_2M
0045 #define GEN9_WOPCM_SIZE         SZ_1M
0046 #define MAX_WOPCM_SIZE          SZ_8M
0047 /* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
0048 #define WOPCM_RESERVED_SIZE     SZ_16K
0049 
0050 /* 16KB reserved at the beginning of GuC WOPCM. */
0051 #define GUC_WOPCM_RESERVED      SZ_16K
0052 /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
0053 #define GUC_WOPCM_STACK_RESERVED    SZ_8K
0054 
0055 /* GuC WOPCM Offset value needs to be aligned to 16KB. */
0056 #define GUC_WOPCM_OFFSET_ALIGNMENT  (1UL << GUC_WOPCM_OFFSET_SHIFT)
0057 
0058 /* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
0059 #define BXT_WOPCM_RC6_CTX_RESERVED  (SZ_16K + SZ_8K)
0060 /* 36KB WOPCM reserved at the end of WOPCM on ICL. */
0061 #define ICL_WOPCM_HW_CTX_RESERVED   (SZ_32K + SZ_4K)
0062 
0063 /* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
0064 #define GEN9_GUC_FW_RESERVED    SZ_128K
0065 #define GEN9_GUC_WOPCM_OFFSET   (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
0066 
0067 static inline struct drm_i915_private *wopcm_to_i915(struct intel_wopcm *wopcm)
0068 {
0069     return container_of(wopcm, struct drm_i915_private, wopcm);
0070 }
0071 
0072 /**
0073  * intel_wopcm_init_early() - Early initialization of the WOPCM.
0074  * @wopcm: pointer to intel_wopcm.
0075  *
0076  * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
0077  */
0078 void intel_wopcm_init_early(struct intel_wopcm *wopcm)
0079 {
0080     struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
0081 
0082     if (!HAS_GT_UC(i915))
0083         return;
0084 
0085     if (GRAPHICS_VER(i915) >= 11)
0086         wopcm->size = GEN11_WOPCM_SIZE;
0087     else
0088         wopcm->size = GEN9_WOPCM_SIZE;
0089 
0090     drm_dbg(&i915->drm, "WOPCM: %uK\n", wopcm->size / 1024);
0091 }
0092 
0093 static u32 context_reserved_size(struct drm_i915_private *i915)
0094 {
0095     if (IS_GEN9_LP(i915))
0096         return BXT_WOPCM_RC6_CTX_RESERVED;
0097     else if (GRAPHICS_VER(i915) >= 11)
0098         return ICL_WOPCM_HW_CTX_RESERVED;
0099     else
0100         return 0;
0101 }
0102 
0103 static bool gen9_check_dword_gap(struct drm_i915_private *i915,
0104                  u32 guc_wopcm_base, u32 guc_wopcm_size)
0105 {
0106     u32 offset;
0107 
0108     /*
0109      * GuC WOPCM size shall be at least a dword larger than the offset from
0110      * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
0111      * due to hardware limitation on Gen9.
0112      */
0113     offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
0114     if (offset > guc_wopcm_size ||
0115         (guc_wopcm_size - offset) < sizeof(u32)) {
0116         drm_err(&i915->drm,
0117             "WOPCM: invalid GuC region size: %uK < %uK\n",
0118             guc_wopcm_size / SZ_1K,
0119             (u32)(offset + sizeof(u32)) / SZ_1K);
0120         return false;
0121     }
0122 
0123     return true;
0124 }
0125 
0126 static bool gen9_check_huc_fw_fits(struct drm_i915_private *i915,
0127                    u32 guc_wopcm_size, u32 huc_fw_size)
0128 {
0129     /*
0130      * On Gen9, hardware requires the total available GuC WOPCM
0131      * size to be larger than or equal to HuC firmware size. Otherwise,
0132      * firmware uploading would fail.
0133      */
0134     if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
0135         drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
0136             intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
0137             (guc_wopcm_size - GUC_WOPCM_RESERVED) / SZ_1K,
0138             huc_fw_size / 1024);
0139         return false;
0140     }
0141 
0142     return true;
0143 }
0144 
0145 static bool check_hw_restrictions(struct drm_i915_private *i915,
0146                   u32 guc_wopcm_base, u32 guc_wopcm_size,
0147                   u32 huc_fw_size)
0148 {
0149     if (GRAPHICS_VER(i915) == 9 && !gen9_check_dword_gap(i915, guc_wopcm_base,
0150                                  guc_wopcm_size))
0151         return false;
0152 
0153     if (GRAPHICS_VER(i915) == 9 &&
0154         !gen9_check_huc_fw_fits(i915, guc_wopcm_size, huc_fw_size))
0155         return false;
0156 
0157     return true;
0158 }
0159 
0160 static bool __check_layout(struct drm_i915_private *i915, u32 wopcm_size,
0161                u32 guc_wopcm_base, u32 guc_wopcm_size,
0162                u32 guc_fw_size, u32 huc_fw_size)
0163 {
0164     const u32 ctx_rsvd = context_reserved_size(i915);
0165     u32 size;
0166 
0167     size = wopcm_size - ctx_rsvd;
0168     if (unlikely(range_overflows(guc_wopcm_base, guc_wopcm_size, size))) {
0169         drm_err(&i915->drm,
0170             "WOPCM: invalid GuC region layout: %uK + %uK > %uK\n",
0171             guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K,
0172             size / SZ_1K);
0173         return false;
0174     }
0175 
0176     size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
0177     if (unlikely(guc_wopcm_size < size)) {
0178         drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
0179             intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_GUC),
0180             guc_wopcm_size / SZ_1K, size / SZ_1K);
0181         return false;
0182     }
0183 
0184     size = huc_fw_size + WOPCM_RESERVED_SIZE;
0185     if (unlikely(guc_wopcm_base < size)) {
0186         drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
0187             intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
0188             guc_wopcm_base / SZ_1K, size / SZ_1K);
0189         return false;
0190     }
0191 
0192     return check_hw_restrictions(i915, guc_wopcm_base, guc_wopcm_size,
0193                      huc_fw_size);
0194 }
0195 
0196 static bool __wopcm_regs_locked(struct intel_uncore *uncore,
0197                 u32 *guc_wopcm_base, u32 *guc_wopcm_size)
0198 {
0199     u32 reg_base = intel_uncore_read(uncore, DMA_GUC_WOPCM_OFFSET);
0200     u32 reg_size = intel_uncore_read(uncore, GUC_WOPCM_SIZE);
0201 
0202     if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) ||
0203         !(reg_base & GUC_WOPCM_OFFSET_VALID))
0204         return false;
0205 
0206     *guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK;
0207     *guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK;
0208     return true;
0209 }
0210 
0211 static bool __wopcm_regs_writable(struct intel_uncore *uncore)
0212 {
0213     if (!HAS_GUC_DEPRIVILEGE(uncore->i915))
0214         return true;
0215 
0216     return intel_uncore_read(uncore, GUC_SHIM_CONTROL2) & GUC_IS_PRIVILEGED;
0217 }
0218 
0219 /**
0220  * intel_wopcm_init() - Initialize the WOPCM structure.
0221  * @wopcm: pointer to intel_wopcm.
0222  *
0223  * This function will partition WOPCM space based on GuC and HuC firmware sizes
0224  * and will allocate max remaining for use by GuC. This function will also
0225  * enforce platform dependent hardware restrictions on GuC WOPCM offset and
0226  * size. It will fail the WOPCM init if any of these checks fail, so that the
0227  * following WOPCM registers setup and GuC firmware uploading would be aborted.
0228  */
0229 void intel_wopcm_init(struct intel_wopcm *wopcm)
0230 {
0231     struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
0232     struct intel_gt *gt = to_gt(i915);
0233     u32 guc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.guc.fw);
0234     u32 huc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.huc.fw);
0235     u32 ctx_rsvd = context_reserved_size(i915);
0236     u32 wopcm_size = wopcm->size;
0237     u32 guc_wopcm_base;
0238     u32 guc_wopcm_size;
0239 
0240     if (!guc_fw_size)
0241         return;
0242 
0243     GEM_BUG_ON(!wopcm_size);
0244     GEM_BUG_ON(wopcm->guc.base);
0245     GEM_BUG_ON(wopcm->guc.size);
0246     GEM_BUG_ON(guc_fw_size >= wopcm_size);
0247     GEM_BUG_ON(huc_fw_size >= wopcm_size);
0248     GEM_BUG_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm_size);
0249 
0250     if (i915_inject_probe_failure(i915))
0251         return;
0252 
0253     if (__wopcm_regs_locked(gt->uncore, &guc_wopcm_base, &guc_wopcm_size)) {
0254         drm_dbg(&i915->drm, "GuC WOPCM is already locked [%uK, %uK)\n",
0255             guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
0256         /*
0257          * Note that to keep things simple (i.e. avoid different
0258          * defines per platform) our WOPCM math doesn't always use the
0259          * actual WOPCM size, but a value that is less or equal to it.
0260          * This is perfectly fine when i915 programs the registers, but
0261          * on platforms with GuC deprivilege the registers are not
0262          * writable from i915 and are instead pre-programmed by the
0263          * bios/IFWI, so there might be a mismatch of sizes.
0264          * Instead of handling the size difference, we trust that the
0265          * programmed values make sense and disable the relevant check
0266          * by using the maximum possible WOPCM size in the verification
0267          * math. In the extremely unlikely case that the registers
0268          * were pre-programmed with an invalid value, we will still
0269          * gracefully fail later during the GuC/HuC dma.
0270          */
0271         if (!__wopcm_regs_writable(gt->uncore))
0272             wopcm_size = MAX_WOPCM_SIZE;
0273 
0274         goto check;
0275     }
0276 
0277     /*
0278      * Aligned value of guc_wopcm_base will determine available WOPCM space
0279      * for HuC firmware and mandatory reserved area.
0280      */
0281     guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE;
0282     guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT);
0283 
0284     /*
0285      * Need to clamp guc_wopcm_base now to make sure the following math is
0286      * correct. Formal check of whole WOPCM layout will be done below.
0287      */
0288     guc_wopcm_base = min(guc_wopcm_base, wopcm_size - ctx_rsvd);
0289 
0290     /* Aligned remainings of usable WOPCM space can be assigned to GuC. */
0291     guc_wopcm_size = wopcm_size - ctx_rsvd - guc_wopcm_base;
0292     guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
0293 
0294     drm_dbg(&i915->drm, "Calculated GuC WOPCM [%uK, %uK)\n",
0295         guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
0296 
0297 check:
0298     if (__check_layout(i915, wopcm_size, guc_wopcm_base, guc_wopcm_size,
0299                guc_fw_size, huc_fw_size)) {
0300         wopcm->guc.base = guc_wopcm_base;
0301         wopcm->guc.size = guc_wopcm_size;
0302         GEM_BUG_ON(!wopcm->guc.base);
0303         GEM_BUG_ON(!wopcm->guc.size);
0304     }
0305 }