[Intel-gfx] [PATCH] drm/i915: Add GuC css header parser
Daniel Vetter
daniel at ffwll.ch
Fri Sep 4 01:13:28 PDT 2015
On Wed, Sep 02, 2015 at 03:52:35PM -0700, yu.dai at intel.com wrote:
> From: Alex Dai <yu.dai at intel.com>
>
> By using information from GuC css header, we can eliminate some
> hard code w.r.t size of some components of firmware.
>
> Signed-off-by: Alex Dai <yu.dai at intel.com>
> ---
> drivers/gpu/drm/i915/intel_guc.h | 2 +-
> drivers/gpu/drm/i915/intel_guc_fwif.h | 36 +++++++++++++
> drivers/gpu/drm/i915/intel_guc_loader.c | 91 ++++++++++++++++++++++-----------
> 3 files changed, 98 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
> index 4ec2d27..e1389fc 100644
> --- a/drivers/gpu/drm/i915/intel_guc.h
> +++ b/drivers/gpu/drm/i915/intel_guc.h
> @@ -71,6 +71,7 @@ struct intel_guc_fw {
> struct drm_i915_gem_object * guc_fw_obj;
> enum intel_guc_fw_status guc_fw_fetch_status;
> enum intel_guc_fw_status guc_fw_load_status;
> + struct guc_css_header guc_fw_header;
>
> uint16_t guc_fw_major_wanted;
> uint16_t guc_fw_minor_wanted;
> @@ -80,7 +81,6 @@ struct intel_guc_fw {
>
> struct intel_guc {
> struct intel_guc_fw guc_fw;
> -
> uint32_t log_flags;
> struct drm_i915_gem_object *log_obj;
>
> diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h
> index e1f47ba..d6cb4e8 100644
> --- a/drivers/gpu/drm/i915/intel_guc_fwif.h
> +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h
> @@ -122,6 +122,42 @@
>
> #define GUC_CTL_MAX_DWORDS (GUC_CTL_RSRVD + 1)
>
> +struct guc_css_header {
> + uint32_t module_type;
> + uint32_t header_len; /* header length plus size of all other keys */
> + uint32_t header_version;
> + uint32_t module_id;
> + uint32_t module_vendor;
> + union {
> + struct {
> + uint8_t day;
> + uint8_t month;
> + uint16_t year;
> + };
> + uint32_t date;
> + };
> + uint32_t size; /* uCode size plus header_len */
> + uint32_t key_size;
> + uint32_t modulus_size;
> + uint32_t exponent_size;
> + union {
> + struct {
> + uint8_t hour;
> + uint8_t min;
> + uint16_t sec;
> + };
> + uint32_t time;
> + };
> +
> + char username[8];
> + char buildnumber[12];
> + uint32_t device_id;
> + uint32_t guc_sw_version;
> + uint32_t prod_preprod_fw;
> + uint32_t reserved[12];
> + uint32_t header_info;
> +};
> +
> struct guc_doorbell_info {
> u32 db_status;
> u32 cookie;
> diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c
> index 5823615..96826ae 100644
> --- a/drivers/gpu/drm/i915/intel_guc_loader.c
> +++ b/drivers/gpu/drm/i915/intel_guc_loader.c
> @@ -215,18 +215,24 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
> ((val & GS_MIA_CORE_STATE) && uk_val == GS_UKERNEL_LAPIC_DONE));
> }
>
> -/*
> +/**
You need at least a DOC: here plus add a line to pull this into the
drm.tmpl for kerneldoc to pick this up. Otherwise it'll complain. But I'd
really like this to be in the docbook, that's for sure.
-Daniel
> * Transfer the firmware image to RAM for execution by the microcontroller.
> *
> * GuC Firmware layout:
> - * +-------------------------------+ ----
> - * | CSS header | 128B
> + * +-------------------------------+
> + * | guc_css_header |
> * | contains major/minor version |
> - * +-------------------------------+ ----
> + * +-------------------------------+
> * | uCode |
> - * +-------------------------------+ ----
> - * | RSA signature | 256B
> - * +-------------------------------+ ----
> + * +-------------------------------+
> + * | RSA signature |
> + * +-------------------------------+
> + * | modulus key |
> + * +-------------------------------+
> + * | exponent val |
> + * +-------------------------------+
> + * The firmware may or may not have modulus key and exponent data. The header,
> + * uCode and RSA signature are must-have components that will be used by driver.
> *
> * Architecturally, the DMA engine is bidirectional, and can potentially even
> * transfer between GTT locations. This functionality is left out of the API
> @@ -236,30 +242,39 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
> * 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 guc_css_header *header = &guc_fw->guc_fw_header;
> 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, header_size, rsa_size, ucode_size, *rsa;
> 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);
> + /* The header plus uCode will be copied to WOPCM via DMA, excluding any
> + * other components */
> + header_size = (header->header_len - header->key_size -
> + header->modulus_size - header->exponent_size) * sizeof(u32);
> + ucode_size = (header->size - header->header_len) * sizeof(u32);
> +
> + I915_WRITE(DMA_COPY_SIZE, header_size + ucode_size);
> +
> + /* where RSA signature starts */
> + offset = header_size + ucode_size;
> +
> + rsa_size = header->key_size * sizeof(u32);
> + rsa = kmalloc(rsa_size, GFP_KERNEL);
> + if (!rsa)
> + return -ENOMEM;
>
> /* 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, rsa_size, offset);
> + for (i = 0; i < rsa_size / sizeof(u32); i++)
> I915_WRITE(UOS_RSA_SCRATCH_0 + i * sizeof(u32), rsa[i]);
>
> + kfree(rsa);
> +
> /* Set the source address for the new blob */
> offset = i915_gem_obj_ggtt_offset(fw_obj);
> I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
> @@ -458,10 +473,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_header = &guc_fw->guc_fw_header;
> + size_t size;
> int err;
>
> DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n",
> @@ -475,12 +488,31 @@ 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;
> + }
> +
> + memcpy(css_header, fw->data, sizeof(struct guc_css_header));
> +
> + /* At least, it should have header, uCode and RSA. Size of all three. */
> + size = (css_header->size - css_header->modulus_size -
> + css_header->exponent_size) * sizeof(u32);
> + 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 -= css_header->key_size * sizeof(u32);
> +
> + /* 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
> @@ -488,9 +520,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_header->guc_sw_version >> 16;
> + guc_fw->guc_fw_minor_found = css_header->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) {
> --
> 1.9.1
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the Intel-gfx
mailing list