drm/i915/guc: Add GuC css header parser
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_guc_loader.c
index ac31696012df0f11d76caa6816f826ace90d440d..c0281df52702e0427ec789053b163bbf2e8d970b 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);
 
@@ -457,10 +443,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 +458,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 +511,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) {
This page took 0.026244 seconds and 5 git commands to generate.