[Intel-xe] [PATCH 05/12] drm/xe/gsc: Parse GSC FW header
Daniele Ceraolo Spurio
daniele.ceraolospurio at intel.com
Tue Nov 7 23:57:46 UTC 2023
On 11/7/2023 3:45 PM, John Harrison wrote:
> On 10/27/2023 15:29, Daniele Ceraolo Spurio wrote:
>> The GSC blob starts with a layout header, from which we can move to the
>> boot directory, which in turns allows us to find the CPD. The CPD uses
>> the same format as the one in the HuC binary, so we can re-use the same
>> parsing code to get to the manifest, which contains the release and
>> security versions of the FW.
>>
>> Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio at intel.com>
>> Cc: Alan Previn <alan.previn.teres.alexis at intel.com>
>> Cc: John Harrison <John.C.Harrison at Intel.com>
>> Cc: Lucas De Marchi <lucas.demarchi at intel.com>
>> ---
>> drivers/gpu/drm/xe/xe_gsc_types.h | 3 +
>> drivers/gpu/drm/xe/xe_uc_fw.c | 77 ++++++++++++++++++++
>> drivers/gpu/drm/xe/xe_uc_fw_abi.h | 113 ++++++++++++++++++++++++++++++
>> 3 files changed, 193 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_gsc_types.h
>> b/drivers/gpu/drm/xe/xe_gsc_types.h
>> index 135f156e3736..1bc50583fe58 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc_types.h
>> +++ b/drivers/gpu/drm/xe/xe_gsc_types.h
>> @@ -14,6 +14,9 @@
>> struct xe_gsc {
>> /** @fw: Generic uC firmware management */
>> struct xe_uc_fw fw;
>> +
>> + /** @security_version: SVN found in the fetched blob */
>> + u32 security_version;
> There is no official structure to this version number?
no, it's just a linearly increasing integer, there are no
minor/patch/build versions involved.
>
>> };
>> #endif
>> diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c
>> b/drivers/gpu/drm/xe/xe_uc_fw.c
>> index af3e5cba606f..bb38a76eb4a6 100644
>> --- a/drivers/gpu/drm/xe/xe_uc_fw.c
>> +++ b/drivers/gpu/drm/xe/xe_uc_fw.c
>> @@ -12,6 +12,7 @@
>> #include "xe_bo.h"
>> #include "xe_device_types.h"
>> #include "xe_force_wake.h"
>> +#include "xe_gsc.h"
>> #include "xe_gt.h"
>> #include "xe_map.h"
>> #include "xe_mmio.h"
>> @@ -488,6 +489,13 @@ static int parse_cpd_header(struct xe_uc_fw
>> *uc_fw, const void *data, size_t siz
>> release->minor = manifest->fw_version.minor;
>> release->patch = manifest->fw_version.hotfix;
>> + if (uc_fw->type == XE_UC_FW_TYPE_GSC) {
>> + struct xe_gsc * gsc = container_of(uc_fw, struct xe_gsc, fw);
>> +
>> + release->build = manifest->fw_version.build;
>> + gsc->security_version = manifest->security_version;
>> + }
>> +
>> /* then optionally look for the css header */
>> if (css_entry) {
>> int ret;
>> @@ -517,6 +525,73 @@ static int parse_cpd_header(struct xe_uc_fw
>> *uc_fw, const void *data, size_t siz
>> return 0;
>> }
>> +static int parse_gsc_layout(struct xe_uc_fw *uc_fw, const void
>> *data, size_t size)
>> +{
>> + struct xe_gt *gt = uc_fw_to_gt(uc_fw);
>> + const struct gsc_layout_pointers *layout = data;
>> + const struct gsc_bpdt_header *bpdt_header = NULL;
>> + const struct gsc_bpdt_entry *bpdt_entry = NULL;
>> + size_t min_size = sizeof(*layout);
>> + int i;
>> +
>> + if (size < min_size) {
>> + xe_gt_err(gt, "GSC FW too small! %zu < %zu\n", size, min_size);
>> + return -ENODATA;
>> + }
>> +
>> + min_size = layout->boot1.offset + layout->boot1.size;
>> + if (size < min_size) {
>> + xe_gt_err(gt, "GSC FW too small for boot section! %zu < %zu\n",
>> + size, min_size);
>> + return -ENODATA;
>> + }
>> +
>> + min_size = sizeof(*bpdt_header);
>> + if (layout->boot1.size < min_size) {
>> + xe_gt_err(gt, "GSC FW boot section too small for BPDT
>> header: %u < %zu\n",
>> + layout->boot1.size, min_size);
>> + return -ENODATA;
>> + }
>> +
>> + bpdt_header = data + layout->boot1.offset;
>> + if (bpdt_header->signature != GSC_BPDT_HEADER_SIGNATURE) {
>> + xe_gt_err(gt, "invalid signature for BPDT header: 0x%08x!\n",
>> + bpdt_header->signature);
>> + return -EINVAL;
>> + }
>> +
>> + min_size += sizeof(*bpdt_entry) * bpdt_header->descriptor_count;
>> + if (layout->boot1.size < min_size) {
>> + xe_gt_err(gt, "GSC FW boot section too small for BPDT
>> entries: %u < %zu\n",
>> + layout->boot1.size, min_size);
>> + return -ENODATA;
>> + }
>> +
>> + bpdt_entry = (void *)bpdt_header + sizeof(*bpdt_header);
>> + for (i = 0; i < bpdt_header->descriptor_count; i++, bpdt_entry++) {
>> + if ((bpdt_entry->type & GSC_BPDT_ENTRY_TYPE_MASK) !=
>> + GSC_BPDT_ENTRY_TYPE_GSC_RBE)
>> + continue;
>> +
>> + min_size = bpdt_entry->sub_partition_offset;
>> +
>> + /* the CPD header parser will check that the CPD header fits */
>> + if (layout->boot1.size < min_size) {
>> + xe_gt_err(gt, "GSC FW boot section too small for CPD
>> offset: %u < %zu\n",
>> + layout->boot1.size, min_size);
>> + return -ENODATA;
>> + }
>> +
>> + return parse_cpd_header(uc_fw,
>> + (void *)bpdt_header + min_size,
>> + layout->boot1.size - min_size,
> Could compare this calculation against bpdt_entry->sub_partition_size
> before bothering to try to decode a partial CPD?
The CPD parser checks for the size. I could add an extra check here and
bail early, but why add extra case to optimize a scenario that should
never happen?
>
>> + "RBEP.man", NULL);
>> + }
>> +
>> + xe_gt_err(gt, "couldn't find CPD header in GSC binary!\n");
>> + return -ENODATA;
>> +}
>> +
>> static int parse_headers(struct xe_uc_fw *uc_fw, const struct
>> firmware *fw)
>> {
>> int ret;
>> @@ -526,6 +601,8 @@ static int parse_headers(struct xe_uc_fw *uc_fw,
>> const struct firmware *fw)
>> * releases use GSC CPD headers.
>> */
>> switch (uc_fw->type) {
>> + case XE_UC_FW_TYPE_GSC:
>> + return parse_gsc_layout(uc_fw, fw->data, fw->size);
>> case XE_UC_FW_TYPE_HUC:
>> ret = parse_cpd_header(uc_fw, fw->data, fw->size,
>> "HUCP.man", "huc_fw");
>> if (!ret || ret != -ENOENT)
>> diff --git a/drivers/gpu/drm/xe/xe_uc_fw_abi.h
>> b/drivers/gpu/drm/xe/xe_uc_fw_abi.h
>> index d6725c963251..edf2a448f4bb 100644
>> --- a/drivers/gpu/drm/xe/xe_uc_fw_abi.h
>> +++ b/drivers/gpu/drm/xe/xe_uc_fw_abi.h
>> @@ -140,6 +140,58 @@ static_assert(sizeof(struct uc_css_header) == 128);
>> * | RSA Key (MTL+ only) |
>> * | ... |
>> * +================================================+
>> + *
>> + * The GSC binary starts instead with a layout header, which
>> contains the
>> + * locations of the various partitions of the binary. The one we're
>> interested
>> + * in is the boot1 partition, where we can find a BPDT header
>> followed by
>> + * entries, one of which points to the RBE sub-section of the
>> partition, which
>> + * contains the CPD. The GSC blob does not contain a CSS-based
>> binary, so we
>> + * only need to look for the manifest, which is under the "RBEP.man"
>> CPD entry.
>> + * Note that we have no need to find where the actual FW code is
>> inside the
>> + * image because the GSC ROM will itself parse the headers to find
>> it and load
>> + * it.
>> + * The GSC firmware header layout looks like this::
>> + *
>> + * +================================================+
>> + * | Layout Pointers |
>> + * | ... |
>> + * | Boot1 offset >---------------------------|------o
>> + * | ... | |
>> + * +================================================+ |
>> + * |
>> + * +================================================+ |
>> + * | BPDT header |<-----o
>> + * +================================================+
>> + * | BPDT entries[] |
>> + * | entry1 |
>> + * | ... |
>> + * | entryX |
>> + * | type == GSC_RBE |
>> + * | offset >-----------------------------|------o
>> + * | ... | |
>> + * +================================================+ |
>> + * |
>> + * +================================================+ |
>> + * | CPD Header |<-----o
>> + * +================================================+
>> + * | CPD entries[] |
>> + * | entry1 |
>> + * | ... |
>> + * | entryX |
>> + * | "RBEP.man" |
>> + * | ... |
>> + * | offset >----------------------------|------o
>> + * | ... | |
>> + * +================================================+ |
>> + * |
>> + * +================================================+ |
>> + * | Manifest Header |<-----o
>> + * | ... |
>> + * | FW version |
>> + * | ... |
>> + * | Security version |
>> + * | ... |
>> + * +================================================+
>> */
>> struct gsc_version {
>> @@ -149,6 +201,67 @@ struct gsc_version {
>> u16 build;
>> } __packed;
>> +struct gsc_partition {
>> + u32 offset;
>> + u32 size;
>> +} __packed;
>> +
>> +struct gsc_layout_pointers {
>> + u8 rom_bypass_vector[16];
>> +
>> + /* size of pointers layout not including ROM bypass vector */
> pointers layout -> layout pointers?
I'll just simplify it to "size of this header section"
>
>> + u16 size;
>> +
>> + /*
>> + * bit0: Backup copy of layout pointers exist
> exist -> exists
ack
>
>> + * bits1-15: reserved
>> + */
>> + u8 flags;
>> +
>> + u8 reserved;
>> +
>> + u32 crc32;
> Should we be doing anything to test against this crc or the one in the
> bpdt_header below?
No, the GSC ROM checks those at load time.
Daniele
>
> John.
>
>> +
>> + struct gsc_partition datap;
>> + struct gsc_partition boot1;
>> + struct gsc_partition boot2;
>> + struct gsc_partition boot3;
>> + struct gsc_partition boot4;
>> + struct gsc_partition boot5;
>> + struct gsc_partition temp_pages;
>> +} __packed;
>> +
>> +/* Boot partition structures */
>> +struct gsc_bpdt_header {
>> + u32 signature;
>> +#define GSC_BPDT_HEADER_SIGNATURE 0x000055AA
>> +
>> + u16 descriptor_count; /* num of entries after the header */
>> +
>> + u8 version;
>> + u8 configuration;
>> +
>> + u32 crc32;
>> +
>> + u32 build_version;
>> + struct gsc_version tool_version;
>> +} __packed;
>> +
>> +struct gsc_bpdt_entry {
>> + /*
>> + * Bits 0-15: BPDT entry type
>> + * Bits 16-17: reserved
>> + * Bit 18: code sub-partition
>> + * Bits 19-31: reserved
>> + */
>> + u32 type;
>> +#define GSC_BPDT_ENTRY_TYPE_MASK GENMASK(15, 0)
>> +#define GSC_BPDT_ENTRY_TYPE_GSC_RBE 0x1
>> +
>> + u32 sub_partition_offset; /* from the base of the BPDT header */
>> + u32 sub_partition_size;
>> +} __packed;
>> +
>> /* Code partition directory (CPD) structures */
>> struct gsc_cpd_header_v2 {
>> u32 header_marker;
>
More information about the Intel-xe
mailing list