]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/xe/xe_late_bind_fw: Extract and print version info
authorBadal Nilawar <badal.nilawar@intel.com>
Fri, 5 Sep 2025 15:49:53 +0000 (21:19 +0530)
committerLucas De Marchi <lucas.demarchi@intel.com>
Thu, 18 Sep 2025 16:32:01 +0000 (09:32 -0700)
Extract and print version info of the late binding binary.

v2: Some refinements (Daniele)

Signed-off-by: Badal Nilawar <badal.nilawar@intel.com>
Reviewed-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://lore.kernel.org/r/20250905154953.3974335-10-badal.nilawar@intel.com
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
drivers/gpu/drm/xe/xe_late_bind_fw.c
drivers/gpu/drm/xe/xe_late_bind_fw_types.h
drivers/gpu/drm/xe/xe_uc_fw_abi.h

index 0f062008ca836606556286dc167dbb427fdb34b1..38f3feb2aecd1fd086fd3c0d9e786781f3f0b544 100644 (file)
@@ -45,6 +45,121 @@ late_bind_to_xe(struct xe_late_bind *late_bind)
        return container_of(late_bind, struct xe_device, late_bind);
 }
 
+static struct xe_device *
+late_bind_fw_to_xe(struct xe_late_bind_fw *lb_fw)
+{
+       return container_of(lb_fw, struct xe_device, late_bind.late_bind_fw[lb_fw->id]);
+}
+
+/* Refer to the "Late Bind based Firmware Layout" documentation entry for details */
+static int parse_cpd_header(struct xe_late_bind_fw *lb_fw,
+                           const void *data, size_t size, const char *manifest_entry)
+{
+       struct xe_device *xe = late_bind_fw_to_xe(lb_fw);
+       const struct gsc_cpd_header_v2 *header = data;
+       const struct gsc_manifest_header *manifest;
+       const struct gsc_cpd_entry *entry;
+       size_t min_size = sizeof(*header);
+       u32 offset;
+       int i;
+
+       /* manifest_entry is mandatory */
+       xe_assert(xe, manifest_entry);
+
+       if (size < min_size || header->header_marker != GSC_CPD_HEADER_MARKER)
+               return -ENOENT;
+
+       if (header->header_length < sizeof(struct gsc_cpd_header_v2)) {
+               drm_err(&xe->drm, "%s late binding fw: Invalid CPD header length %u!\n",
+                       fw_id_to_name[lb_fw->id], header->header_length);
+               return -EINVAL;
+       }
+
+       min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries;
+       if (size < min_size) {
+               drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
+                       fw_id_to_name[lb_fw->id], size, min_size);
+               return -ENODATA;
+       }
+
+       /* Look for the manifest first */
+       entry = (void *)header + header->header_length;
+       for (i = 0; i < header->num_of_entries; i++, entry++)
+               if (strcmp(entry->name, manifest_entry) == 0)
+                       offset = entry->offset & GSC_CPD_ENTRY_OFFSET_MASK;
+
+       if (!offset) {
+               drm_err(&xe->drm, "%s late binding fw: Failed to find manifest_entry\n",
+                       fw_id_to_name[lb_fw->id]);
+               return -ENODATA;
+       }
+
+       min_size = offset + sizeof(struct gsc_manifest_header);
+       if (size < min_size) {
+               drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
+                       fw_id_to_name[lb_fw->id], size, min_size);
+               return -ENODATA;
+       }
+
+       manifest = data + offset;
+
+       lb_fw->version = manifest->fw_version;
+
+       return 0;
+}
+
+/* Refer to the "Late Bind based Firmware Layout" documentation entry for details */
+static int parse_lb_layout(struct xe_late_bind_fw *lb_fw,
+                          const void *data, size_t size, const char *fpt_entry)
+{
+       struct xe_device *xe = late_bind_fw_to_xe(lb_fw);
+       const struct csc_fpt_header *header = data;
+       const struct csc_fpt_entry *entry;
+       size_t min_size = sizeof(*header);
+       u32 offset;
+       int i;
+
+       /* fpt_entry is mandatory */
+       xe_assert(xe, fpt_entry);
+
+       if (size < min_size || header->header_marker != CSC_FPT_HEADER_MARKER)
+               return -ENOENT;
+
+       if (header->header_length < sizeof(struct csc_fpt_header)) {
+               drm_err(&xe->drm, "%s late binding fw: Invalid FPT header length %u!\n",
+                       fw_id_to_name[lb_fw->id], header->header_length);
+               return -EINVAL;
+       }
+
+       min_size = header->header_length + sizeof(struct csc_fpt_entry) * header->num_of_entries;
+       if (size < min_size) {
+               drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
+                       fw_id_to_name[lb_fw->id], size, min_size);
+               return -ENODATA;
+       }
+
+       /* Look for the cpd header first */
+       entry = (void *)header + header->header_length;
+       for (i = 0; i < header->num_of_entries; i++, entry++)
+               if (strcmp(entry->name, fpt_entry) == 0)
+                       offset = entry->offset;
+
+       if (!offset) {
+               drm_err(&xe->drm, "%s late binding fw: Failed to find fpt_entry\n",
+                       fw_id_to_name[lb_fw->id]);
+               return -ENODATA;
+       }
+
+       min_size = offset + sizeof(struct gsc_cpd_header_v2);
+       if (size < min_size) {
+               drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
+                       fw_id_to_name[lb_fw->id], size, min_size);
+               return -ENODATA;
+       }
+
+       return parse_cpd_header(lb_fw, data + offset, size - offset, "LTES.man");
+}
+
 static const char *xe_late_bind_parse_status(uint32_t status)
 {
        switch (status) {
@@ -224,6 +339,10 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
                return -ENODATA;
        }
 
+       ret = parse_lb_layout(lb_fw, fw->data, fw->size, "LTES");
+       if (ret)
+               return ret;
+
        lb_fw->payload_size = fw->size;
        lb_fw->payload = drmm_kzalloc(&xe->drm, lb_fw->payload_size, GFP_KERNEL);
        if (!lb_fw->payload) {
@@ -231,6 +350,11 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
                return -ENOMEM;
        }
 
+       drm_info(&xe->drm, "Using %s firmware from %s version %u.%u.%u.%u\n",
+                fw_id_to_name[lb_fw->id], lb_fw->blob_path,
+                lb_fw->version.major, lb_fw->version.minor,
+                lb_fw->version.hotfix, lb_fw->version.build);
+
        memcpy((void *)lb_fw->payload, fw->data, lb_fw->payload_size);
        release_firmware(fw);
        INIT_WORK(&lb_fw->work, xe_late_bind_work);
index 158dc1abe0724f9cbaf39b1578a16b604222cd21..0f5da89ce98b8ab5e630d32c391433a293316100 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mutex.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
+#include "xe_uc_fw_abi.h"
 
 #define XE_LB_MAX_PAYLOAD_SIZE SZ_4K
 
@@ -39,6 +40,8 @@ struct xe_late_bind_fw {
        size_t payload_size;
        /** @work: worker to upload latebind blob */
        struct work_struct work;
+       /** @version: late binding blob manifest version */
+       struct gsc_version version;
 };
 
 /**
index faceb437fd2f7555b2c7163078d5f62f1f0ddaa1..3c9a63d13032af09092d56684d1414587d68b49b 100644 (file)
@@ -336,4 +336,70 @@ struct gsc_manifest_header {
        u32 exponent_size; /* in dwords */
 } __packed;
 
+/**
+ * DOC: Late binding Firmware Layout
+ *
+ * The Late binding binary starts with FPT header, which contains locations
+ * of various partitions of the binary. Here we're interested in finding out
+ * manifest version. To the manifest version, we need to locate CPD header
+ * one of the entry in CPD header points to manifest header. Manifest header
+ * contains the version.
+ *
+ *      +================================================+
+ *      |  FPT Header                                    |
+ *      +================================================+
+ *      |  FPT entries[]                                 |
+ *      |      entry1                                    |
+ *      |      ...                                       |
+ *      |      entryX                                    |
+ *      |          "LTES"                                |
+ *      |          ...                                   |
+ *      |          offset  >-----------------------------|------o
+ *      +================================================+      |
+ *                                                              |
+ *      +================================================+      |
+ *      |  CPD Header                                    |<-----o
+ *      +================================================+
+ *      |  CPD entries[]                                 |
+ *      |      entry1                                    |
+ *      |      ...                                       |
+ *      |      entryX                                    |
+ *      |          "LTES.man"                            |
+ *      |           ...                                  |
+ *      |           offset  >----------------------------|------o
+ *      +================================================+      |
+ *                                                              |
+ *      +================================================+      |
+ *      |  Manifest Header                               |<-----o
+ *      |      ...                                       |
+ *      |      FW version                                |
+ *      |      ...                                       |
+ *      +================================================+
+ */
+
+/* FPT Headers */
+struct csc_fpt_header {
+       u32 header_marker;
+#define CSC_FPT_HEADER_MARKER 0x54504624
+       u32 num_of_entries;
+       u8 header_version;
+       u8 entry_version;
+       u8 header_length; /* in bytes */
+       u8 flags;
+       u16 ticks_to_add;
+       u16 tokens_to_add;
+       u32 uma_size;
+       u32 crc32;
+       struct gsc_version fitc_version;
+} __packed;
+
+struct csc_fpt_entry {
+       u8 name[4]; /* partition name */
+       u32 reserved1;
+       u32 offset; /* offset from beginning of CSE region */
+       u32 length; /* partition length in bytes */
+       u32 reserved2[3];
+       u32 partition_flags;
+} __packed;
+
 #endif