Extract and print version info of the late binding binary. Signed-off-by: Badal Nilawar <badal.nila...@intel.com> --- drivers/gpu/drm/xe/xe_late_bind_fw.c | 132 ++++++++++++++++++++- drivers/gpu/drm/xe/xe_late_bind_fw_types.h | 3 + drivers/gpu/drm/xe/xe_uc_fw_abi.h | 69 +++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.c b/drivers/gpu/drm/xe/xe_late_bind_fw.c index 001e526e569a..f71d5825ac5b 100644 --- a/drivers/gpu/drm/xe/xe_late_bind_fw.c +++ b/drivers/gpu/drm/xe/xe_late_bind_fw.c @@ -45,6 +45,129 @@ late_bind_to_xe(struct xe_late_bind *late_bind) return container_of(late_bind, struct xe_device, late_bind); } +/* Refer to the "Late Bind based Firmware Layout" documentation entry for details */ +static int parse_cpd_header(struct xe_late_bind *late_bind, u32 fw_id, + const void *data, size_t size, const char *manifest_entry) +{ + struct xe_device *xe = late_bind_to_xe(late_bind); + 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); + struct xe_late_bind_fw *lb_fw; + u32 offset; + int i; + + if (fw_id >= MAX_FW_ID) + return -EINVAL; + lb_fw = &late_bind->late_bind_fw[fw_id]; + + /* 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.major = manifest->fw_version.major; + lb_fw->version.minor = manifest->fw_version.minor; + lb_fw->version.hotfix = manifest->fw_version.hotfix; + lb_fw->version.build = manifest->fw_version.build; + + return 0; +} + +/* Refer to the "Late Bind based Firmware Layout" documentation entry for details */ +static int parse_lb_layout(struct xe_late_bind *late_bind, u32 fw_id, + const void *data, size_t size, const char *fpt_entry) +{ + struct xe_device *xe = late_bind_to_xe(late_bind); + const struct csc_fpt_header *header = data; + const struct csc_fpt_entry *entry; + size_t min_size = sizeof(*header); + struct xe_late_bind_fw *lb_fw; + u32 offset; + int i; + + if (fw_id >= MAX_FW_ID) + return -EINVAL; + + lb_fw = &late_bind->late_bind_fw[fw_id]; + + /* 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 manifest 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(late_bind, fw_id, data + offset, size - offset, "LTES.man"); +} + static int xe_late_bind_fw_num_fans(struct xe_late_bind *late_bind) { struct xe_device *xe = late_bind_to_xe(late_bind); @@ -185,8 +308,15 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id) return -ENODATA; } - lb_fw->payload_size = fw->size; + ret = parse_lb_layout(late_bind, fw_id, fw->data, fw->size, "LTES"); + if (ret) + return ret; + + drm_info(&xe->drm, "Using %s firmware from %s version %d.%d.%d\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->payload_size = fw->size; memcpy(lb_fw->payload, fw->data, lb_fw->payload_size); release_firmware(fw); INIT_WORK(&lb_fw->work, late_bind_work); diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h index f79f0c0b2c4a..3fc4f350c81f 100644 --- a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h +++ b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h @@ -10,6 +10,7 @@ #include <linux/mutex.h> #include <linux/types.h> #include <linux/workqueue.h> +#include "xe_uc_fw_abi.h" #define MAX_PAYLOAD_SIZE (1024 * 4) @@ -41,6 +42,8 @@ struct xe_late_bind_fw { size_t payload_size; /** @late_bind_fw.work: worker to upload latebind blob */ struct work_struct work; + /** @late_bind_fw.version: late binding blob manifest version */ + struct gsc_version version; }; /** diff --git a/drivers/gpu/drm/xe/xe_uc_fw_abi.h b/drivers/gpu/drm/xe/xe_uc_fw_abi.h index 87ade41209d0..13da2ca96817 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw_abi.h +++ b/drivers/gpu/drm/xe/xe_uc_fw_abi.h @@ -318,4 +318,73 @@ 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; + u16 fitc_major; + u16 fitc_minor; + u16 fitc_hotfix; + u16 fitc_build; +} __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 -- 2.34.1