Merge tag 'v4.4-rc2' into drm-intel-next-queued
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / intel_guc_loader.c
index a17b6a56be83283c990a6f40516c71c8c9e6a61f..550921f2ef7d5073f73d26481a44ebb1b4bbfb4d 100644 (file)
@@ -31,7 +31,7 @@
 #include "intel_guc.h"
 
 /**
- * DOC: GuC
+ * DOC: GuC-specific firmware loader
  *
  * intel_guc:
  * Top level structure of guc. It handles firmware loading and manages client
@@ -208,16 +208,6 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
 /*
  * Transfer the firmware image to RAM for execution by the microcontroller.
  *
- * GuC Firmware layout:
- * +-------------------------------+  ----
- * |          CSS header           |  128B
- * | contains major/minor version  |
- * +-------------------------------+  ----
- * |             uCode             |
- * +-------------------------------+  ----
- * |         RSA signature         |  256B
- * +-------------------------------+  ----
- *
  * Architecturally, the DMA engine is bidirectional, and can potentially even
  * transfer between GTT locations. This functionality is left out of the API
  * for now as there is no need for it.
@@ -225,33 +215,29 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
  * Note that GuC needs the CSS header plus uKernel code to be copied by the
  * DMA engine in one operation, whereas the RSA signature is loaded via MMIO.
  */
-
-#define UOS_CSS_HEADER_OFFSET          0
-#define UOS_VER_MINOR_OFFSET           0x44
-#define UOS_VER_MAJOR_OFFSET           0x46
-#define UOS_CSS_HEADER_SIZE            0x80
-#define UOS_RSA_SIG_SIZE               0x100
-
 static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv)
 {
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
        struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj;
        unsigned long offset;
        struct sg_table *sg = fw_obj->pages;
-       u32 status, ucode_size, rsa[UOS_RSA_SIG_SIZE / sizeof(u32)];
+       u32 status, rsa[UOS_RSA_SCRATCH_MAX_COUNT];
        int i, ret = 0;
 
-       /* uCode size, also is where RSA signature starts */
-       offset = ucode_size = guc_fw->guc_fw_size - UOS_RSA_SIG_SIZE;
-       I915_WRITE(DMA_COPY_SIZE, ucode_size);
+       /* where RSA signature starts */
+       offset = guc_fw->rsa_offset;
 
        /* Copy RSA signature from the fw image to HW for verification */
-       sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, UOS_RSA_SIG_SIZE, offset);
-       for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(u32); i++)
+       sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, sizeof(rsa), offset);
+       for (i = 0; i < UOS_RSA_SCRATCH_MAX_COUNT; i++)
                I915_WRITE(UOS_RSA_SCRATCH(i), rsa[i]);
 
+       /* The header plus uCode will be copied to WOPCM via DMA, excluding any
+        * other components */
+       I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size);
+
        /* Set the source address for the new blob */
-       offset = i915_gem_obj_ggtt_offset(fw_obj);
+       offset = i915_gem_obj_ggtt_offset(fw_obj) + guc_fw->header_offset;
        I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
        I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
 
@@ -322,8 +308,8 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
        I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE);
 
        /* WaDisableMinuteIaClockGating:skl,bxt */
-       if ((IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_B0) ||
-           (IS_BROXTON(dev) && INTEL_REVID(dev) == BXT_REVID_A0)) {
+       if (IS_SKL_REVID(dev, 0, SKL_REVID_B0) ||
+           IS_BXT_REVID(dev, 0, BXT_REVID_A1)) {
                I915_WRITE(GUC_SHIM_CONTROL, (I915_READ(GUC_SHIM_CONTROL) &
                                              ~GUC_ENABLE_MIA_CLOCK_GATING));
        }
@@ -378,6 +364,9 @@ int intel_guc_ucode_load(struct drm_device *dev)
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
        int err = 0;
 
+       if (!i915.enable_guc_submission)
+               return 0;
+
        DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n",
                intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status),
                intel_guc_fw_status_repr(guc_fw->guc_fw_load_status));
@@ -457,10 +446,8 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 {
        struct drm_i915_gem_object *obj;
        const struct firmware *fw;
-       const u8 *css_header;
-       const size_t minsize = UOS_CSS_HEADER_SIZE + UOS_RSA_SIG_SIZE;
-       const size_t maxsize = GUC_WOPCM_SIZE_VALUE + UOS_RSA_SIG_SIZE
-                       - 0x8000; /* 32k reserved (8K stack + 24k context) */
+       struct guc_css_header *css;
+       size_t size;
        int err;
 
        DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n",
@@ -474,12 +461,52 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 
        DRM_DEBUG_DRIVER("fetch GuC fw from %s succeeded, fw %p\n",
                guc_fw->guc_fw_path, fw);
-       DRM_DEBUG_DRIVER("firmware file size %zu (minimum %zu, maximum %zu)\n",
-               fw->size, minsize, maxsize);
 
-       /* Check the size of the blob befoe examining buffer contents */
-       if (fw->size < minsize || fw->size > maxsize)
+       /* Check the size of the blob before examining buffer contents */
+       if (fw->size < sizeof(struct guc_css_header)) {
+               DRM_ERROR("Firmware header is missing\n");
                goto fail;
+       }
+
+       css = (struct guc_css_header *)fw->data;
+
+       /* Firmware bits always start from header */
+       guc_fw->header_offset = 0;
+       guc_fw->header_size = (css->header_size_dw - css->modulus_size_dw -
+               css->key_size_dw - css->exponent_size_dw) * sizeof(u32);
+
+       if (guc_fw->header_size != sizeof(struct guc_css_header)) {
+               DRM_ERROR("CSS header definition mismatch\n");
+               goto fail;
+       }
+
+       /* then, uCode */
+       guc_fw->ucode_offset = guc_fw->header_offset + guc_fw->header_size;
+       guc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
+
+       /* now RSA */
+       if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) {
+               DRM_ERROR("RSA key size is bad\n");
+               goto fail;
+       }
+       guc_fw->rsa_offset = guc_fw->ucode_offset + guc_fw->ucode_size;
+       guc_fw->rsa_size = css->key_size_dw * sizeof(u32);
+
+       /* At least, it should have header, uCode and RSA. Size of all three. */
+       size = guc_fw->header_size + guc_fw->ucode_size + guc_fw->rsa_size;
+       if (fw->size < size) {
+               DRM_ERROR("Missing firmware components\n");
+               goto fail;
+       }
+
+       /* Header and uCode will be loaded to WOPCM. Size of the two. */
+       size = guc_fw->header_size + guc_fw->ucode_size;
+
+       /* Top 32k of WOPCM is reserved (8K stack + 24k RC6 context). */
+       if (size > GUC_WOPCM_SIZE_VALUE - 0x8000) {
+               DRM_ERROR("Firmware is too large to fit in WOPCM\n");
+               goto fail;
+       }
 
        /*
         * The GuC firmware image has the version number embedded at a well-known
@@ -487,9 +514,8 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
         * TWO bytes each (i.e. u16), although all pointers and offsets are defined
         * in terms of bytes (u8).
         */
-       css_header = fw->data + UOS_CSS_HEADER_OFFSET;
-       guc_fw->guc_fw_major_found = *(u16 *)(css_header + UOS_VER_MAJOR_OFFSET);
-       guc_fw->guc_fw_minor_found = *(u16 *)(css_header + UOS_VER_MINOR_OFFSET);
+       guc_fw->guc_fw_major_found = css->guc_sw_version >> 16;
+       guc_fw->guc_fw_minor_found = css->guc_sw_version & 0xFFFF;
 
        if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted ||
            guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) {
@@ -504,7 +530,9 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
                        guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found,
                        guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted);
 
+       mutex_lock(&dev->struct_mutex);
        obj = i915_gem_object_create_from_data(dev, fw->data, fw->size);
+       mutex_unlock(&dev->struct_mutex);
        if (IS_ERR_OR_NULL(obj)) {
                err = obj ? PTR_ERR(obj) : -ENOMEM;
                goto fail;
@@ -540,8 +568,6 @@ fail:
  * @dev:       drm device
  *
  * Called early during driver load, but after GEM is initialised.
- * The device struct_mutex must be held by the caller, as we're
- * going to allocate a GEM object to hold the firmware image.
  *
  * The firmware will be transferred to the GuC's memory later,
  * when intel_guc_ucode_load() is called.
@@ -566,6 +592,9 @@ void intel_guc_ucode_init(struct drm_device *dev)
                fw_path = "";   /* unknown device */
        }
 
+       if (!i915.enable_guc_submission)
+               return;
+
        guc_fw->guc_dev = dev;
        guc_fw->guc_fw_path = fw_path;
        guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE;
@@ -598,9 +627,11 @@ void intel_guc_ucode_fini(struct drm_device *dev)
        direct_interrupts_to_host(dev_priv);
        i915_guc_submission_fini(dev);
 
+       mutex_lock(&dev->struct_mutex);
        if (guc_fw->guc_fw_obj)
                drm_gem_object_unreference(&guc_fw->guc_fw_obj->base);
        guc_fw->guc_fw_obj = NULL;
+       mutex_unlock(&dev->struct_mutex);
 
        guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE;
 }