Commit efa29317 authored by Badal Nilawar's avatar Badal Nilawar Committed by Lucas De Marchi
Browse files

drm/xe/xe_late_bind_fw: Extract and print version info



Extract and print version info of the late binding binary.

v2: Some refinements (Daniele)

Signed-off-by: default avatarBadal Nilawar <badal.nilawar@intel.com>
Reviewed-by: default avatarDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://lore.kernel.org/r/20250905154953.3974335-10-badal.nilawar@intel.com


Signed-off-by: default avatarLucas De Marchi <lucas.demarchi@intel.com>
parent 67de7982
Loading
Loading
Loading
Loading
+124 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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;
};

/**
+66 −0
Original line number Diff line number Diff line
@@ -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