[Intel-gfx] [PATCH v3 2/7] drm/i915/huc: Parse the GSC-enabled HuC binary
John Harrison
john.c.harrison at intel.com
Tue May 30 23:30:42 UTC 2023
On 5/26/2023 17:52, Daniele Ceraolo Spurio wrote:
> The new binaries that support the 2-step authentication contain the
> legacy-style binary, which we can use for loading the HuC via DMA. To
> find out where this is located in the image, we need to parse the
> manifest of the GSC-enabled HuC binary. The manifest consist of a
> partition header followed by entries, one of which contains the offset
> we're looking for.
> Note that the DG2 GSC binary contains entries with the same names, but
> it doesn't contain a full legacy binary, so we need to skip assigning
> the dma offset in that case (which we can do by checking the ccs).
> Also, since we're now parsing the entries, we can extract the HuC
> version that way instead of using hardcoded offsets.
>
> Note that the GSC binary uses the same structures in its binary header,
> so they've been added in their own header file.
>
> v2: fix structure names to match meu defines (s/CPT/CPD/), update commit
> message, check ccs validity, drop old version location defines.
>
> v3: drop references to the MEU tool to reduce confusion, fix log (John)
>
> 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>
> Reviewed-by: Alan Previn <alan.previn.teres.alexis at intel.com> #v2
> ---
> .../drm/i915/gt/uc/intel_gsc_binary_headers.h | 74 ++++++++++
> drivers/gpu/drm/i915/gt/uc/intel_huc.c | 11 +-
> drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c | 135 ++++++++++++++++++
> drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h | 5 +-
> drivers/gpu/drm/i915/gt/uc/intel_huc_print.h | 21 +++
> drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 71 +++++----
> drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h | 2 +
> drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h | 6 -
> 8 files changed, 272 insertions(+), 53 deletions(-)
> create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_gsc_binary_headers.h
> create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_huc_print.h
>
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_gsc_binary_headers.h b/drivers/gpu/drm/i915/gt/uc/intel_gsc_binary_headers.h
> new file mode 100644
> index 000000000000..714f0c256118
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_gsc_binary_headers.h
> @@ -0,0 +1,74 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#ifndef _INTEL_GSC_BINARY_HEADERS_H_
> +#define _INTEL_GSC_BINARY_HEADERS_H_
> +
> +#include <linux/types.h>
> +
> +/* Code partition directory (CPD) structures */
> +struct intel_gsc_cpd_header_v2 {
> + u32 header_marker;
> +#define INTEL_GSC_CPD_HEADER_MARKER 0x44504324
> +
> + u32 num_of_entries;
> + u8 header_version;
> + u8 entry_version;
> + u8 header_length; /* in bytes */
> + u8 flags;
> + u32 partition_name;
> + u32 crc32;
> +} __packed;
> +
> +struct intel_gsc_cpd_entry {
> + u8 name[12];
> +
> + /*
> + * Bits 0-24: offset from the beginning of the code partition
> + * Bit 25: huffman compressed
> + * Bits 26-31: reserved
> + */
> + u32 offset;
> +#define INTEL_GSC_CPD_ENTRY_OFFSET_MASK GENMASK(24, 0)
> +#define INTEL_GSC_CPD_ENTRY_HUFFMAN_COMP BIT(25)
> +
> + /*
> + * Module/Item length, in bytes. For Huffman-compressed modules, this
> + * refers to the uncompressed size. For software-compressed modules,
> + * this refers to the compressed size.
> + */
> + u32 length;
> +
> + u8 reserved[4];
> +} __packed;
> +
> +struct intel_gsc_version {
> + u16 major;
> + u16 minor;
> + u16 hotfix;
> + u16 build;
> +} __packed;
> +
> +struct intel_gsc_manifest_header {
> + u32 header_type; /* 0x4 for manifest type */
> + u32 header_length; /* in dwords */
> + u32 header_version;
> + u32 flags;
> + u32 vendor;
> + u32 date;
> + u32 size; /* In dwords, size of entire manifest (header + extensions) */
> + u32 header_id;
> + u32 internal_data;
> + struct intel_gsc_version fw_version;
> + u32 security_version;
> + struct intel_gsc_version meu_kit_version;
> + u32 meu_manifest_version;
> + u8 general_data[4];
> + u8 reserved3[56];
> + u32 modulus_size; /* in dwords */
> + u32 exponent_size; /* in dwords */
> +} __packed;
> +
> +#endif
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_huc.c b/drivers/gpu/drm/i915/gt/uc/intel_huc.c
> index 268e036f8f28..6d795438b3e4 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_huc.c
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_huc.c
> @@ -6,23 +6,14 @@
> #include <linux/types.h>
>
> #include "gt/intel_gt.h"
> -#include "gt/intel_gt_print.h"
> #include "intel_guc_reg.h"
> #include "intel_huc.h"
> +#include "intel_huc_print.h"
> #include "i915_drv.h"
>
> #include <linux/device/bus.h>
> #include <linux/mei_aux.h>
>
> -#define huc_printk(_huc, _level, _fmt, ...) \
> - gt_##_level(huc_to_gt(_huc), "HuC: " _fmt, ##__VA_ARGS__)
> -#define huc_err(_huc, _fmt, ...) huc_printk((_huc), err, _fmt, ##__VA_ARGS__)
> -#define huc_warn(_huc, _fmt, ...) huc_printk((_huc), warn, _fmt, ##__VA_ARGS__)
> -#define huc_notice(_huc, _fmt, ...) huc_printk((_huc), notice, _fmt, ##__VA_ARGS__)
> -#define huc_info(_huc, _fmt, ...) huc_printk((_huc), info, _fmt, ##__VA_ARGS__)
> -#define huc_dbg(_huc, _fmt, ...) huc_printk((_huc), dbg, _fmt, ##__VA_ARGS__)
> -#define huc_probe_error(_huc, _fmt, ...) huc_printk((_huc), probe_error, _fmt, ##__VA_ARGS__)
> -
> /**
> * DOC: HuC
> *
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c
> index 534b0aa43316..037d2ad4879d 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.c
> @@ -5,11 +5,146 @@
>
> #include "gt/intel_gsc.h"
> #include "gt/intel_gt.h"
> +#include "intel_gsc_binary_headers.h"
> #include "intel_huc.h"
> #include "intel_huc_fw.h"
> +#include "intel_huc_print.h"
> #include "i915_drv.h"
> #include "pxp/intel_pxp_huc.h"
>
> +static void get_version_from_gsc_manifest(struct intel_uc_fw_ver *ver, const void *data)
> +{
> + const struct intel_gsc_manifest_header *manifest = data;
> +
> + ver->major = manifest->fw_version.major;
> + ver->minor = manifest->fw_version.minor;
> + ver->patch = manifest->fw_version.hotfix;
> +}
> +
> +static bool css_valid(const void *data, size_t size)
> +{
> + const struct uc_css_header *css = data;
> +
> + if (unlikely(size < sizeof(struct uc_css_header)))
> + return false;
> +
> + if (css->module_type != 0x6)
> + return false;
> +
> + if (css->module_vendor != PCI_VENDOR_ID_INTEL)
> + return false;
> +
> + return true;
> +}
> +
> +static inline u32 entry_offset(const struct intel_gsc_cpd_entry *entry)
> +{
> + return entry->offset & INTEL_GSC_CPD_ENTRY_OFFSET_MASK;
> +}
> +
> +int intel_huc_fw_get_binary_info(struct intel_uc_fw *huc_fw, const void *data, size_t size)
> +{
> + struct intel_huc *huc = container_of(huc_fw, struct intel_huc, fw);
> + const struct intel_gsc_cpd_header_v2 *header = data;
> + const struct intel_gsc_cpd_entry *entry;
> + size_t min_size = sizeof(*header);
> + int i;
> +
> + if (!huc_fw->loaded_via_gsc) {
> + huc_err(huc, "Invalid FW type GSC header parsing!\n");
I'm still not understanding what this error means. Is it meant to say
'Invalid FW type *for* GSC header parsing'?
'fw->loaded_via_gsc' is set from the blob table, yes? And
intel_huc_fw_get_binary_info() is only called from check_gsc_manifest()
which is called from check_fw_header() iff fw->loaded_via_gsc is set. So
it should be impossible for this test to ever fail, yes?
John.
> + return -EINVAL;
> + }
> +
> + if (size < sizeof(*header)) {
> + huc_err(huc, "FW too small! %zu < %zu\n", size, min_size);
> + return -ENODATA;
> + }
> +
> + /*
> + * The GSC-enabled HuC binary starts with a directory header, followed
> + * by a series of entries. Each entry is identified by a name and
> + * points to a specific section of the binary containing the relevant
> + * data. The entries we're interested in are:
> + * - "HUCP.man": points to the GSC manifest header for the HuC, which
> + * contains the version info.
> + * - "huc_fw": points to the legacy-style binary that can be used for
> + * load via the DMA. This entry only contains a valid CSS
> + * on binaries for platforms that support 2-step HuC load
> + * via dma and auth via GSC (like MTL).
> + *
> + * --------------------------------------------------
> + * [ intel_gsc_cpd_header_v2 ]
> + * --------------------------------------------------
> + * [ intel_gsc_cpd_entry[] ]
> + * [ entry1 ]
> + * [ ... ]
> + * [ entryX ]
> + * [ "HUCP.man" ]
> + * [ ... ]
> + * [ offset >----------------------------]------o
> + * [ ... ] |
> + * [ entryY ] |
> + * [ "huc_fw" ] |
> + * [ ... ] |
> + * [ offset >----------------------------]----------o
> + * -------------------------------------------------- | |
> + * | |
> + * -------------------------------------------------- | |
> + * [ intel_gsc_manifest_header ]<-----o |
> + * [ ... ] |
> + * [ intel_gsc_version fw_version ] |
> + * [ ... ] |
> + * -------------------------------------------------- |
> + * |
> + * -------------------------------------------------- |
> + * [ data[] ]<---------o
> + * [ ... ]
> + * [ ... ]
> + * --------------------------------------------------
> + */
> +
> + if (header->header_marker != INTEL_GSC_CPD_HEADER_MARKER) {
> + huc_err(huc, "invalid marker for CPD header: 0x%08x!\n",
> + header->header_marker);
> + return -EINVAL;
> + }
> +
> + /* we only have binaries with header v2 and entry v1 for now */
> + if (header->header_version != 2 || header->entry_version != 1) {
> + huc_err(huc, "invalid CPD header/entry version %u:%u!\n",
> + header->header_version, header->entry_version);
> + return -EINVAL;
> + }
> +
> + if (header->header_length < sizeof(struct intel_gsc_cpd_header_v2)) {
> + huc_err(huc, "invalid CPD header length %u!\n",
> + header->header_length);
> + return -EINVAL;
> + }
> +
> + min_size = header->header_length + sizeof(*entry) * header->num_of_entries;
> + if (size < min_size) {
> + huc_err(huc, "FW too small! %zu < %zu\n", size, min_size);
> + return -ENODATA;
> + }
> +
> + entry = data + header->header_length;
> +
> + for (i = 0; i < header->num_of_entries; i++, entry++) {
> + if (strcmp(entry->name, "HUCP.man") == 0)
> + get_version_from_gsc_manifest(&huc_fw->file_selected.ver,
> + data + entry_offset(entry));
> +
> + if (strcmp(entry->name, "huc_fw") == 0) {
> + u32 offset = entry_offset(entry);
> + if (offset < size && css_valid(data + offset, size - offset))
> + huc_fw->dma_start_offset = offset;
> + }
> + }
> +
> + return 0;
> +}
> +
> int intel_huc_fw_load_and_auth_via_gsc(struct intel_huc *huc)
> {
> int ret;
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h b/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h
> index db42e238b45f..0999ffe6f962 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_huc_fw.h
> @@ -7,8 +7,11 @@
> #define _INTEL_HUC_FW_H_
>
> struct intel_huc;
> +struct intel_uc_fw;
> +
> +#include <linux/types.h>
>
> int intel_huc_fw_load_and_auth_via_gsc(struct intel_huc *huc);
> int intel_huc_fw_upload(struct intel_huc *huc);
> -
> +int intel_huc_fw_get_binary_info(struct intel_uc_fw *huc_fw, const void *data, size_t size);
> #endif
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_huc_print.h b/drivers/gpu/drm/i915/gt/uc/intel_huc_print.h
> new file mode 100644
> index 000000000000..915d310ee1df
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_huc_print.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#ifndef __INTEL_HUC_PRINT__
> +#define __INTEL_HUC_PRINT__
> +
> +#include "gt/intel_gt.h"
> +#include "gt/intel_gt_print.h"
> +
> +#define huc_printk(_huc, _level, _fmt, ...) \
> + gt_##_level(huc_to_gt(_huc), "HuC: " _fmt, ##__VA_ARGS__)
> +#define huc_err(_huc, _fmt, ...) huc_printk((_huc), err, _fmt, ##__VA_ARGS__)
> +#define huc_warn(_huc, _fmt, ...) huc_printk((_huc), warn, _fmt, ##__VA_ARGS__)
> +#define huc_notice(_huc, _fmt, ...) huc_printk((_huc), notice, _fmt, ##__VA_ARGS__)
> +#define huc_info(_huc, _fmt, ...) huc_printk((_huc), info, _fmt, ##__VA_ARGS__)
> +#define huc_dbg(_huc, _fmt, ...) huc_printk((_huc), dbg, _fmt, ##__VA_ARGS__)
> +#define huc_probe_error(_huc, _fmt, ...) huc_printk((_huc), probe_error, _fmt, ##__VA_ARGS__)
> +
> +#endif /* __INTEL_HUC_PRINT__ */
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
> index 31776c279f32..9ced8dbf1253 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
> @@ -548,33 +548,6 @@ static void __force_fw_fetch_failures(struct intel_uc_fw *uc_fw, int e)
> }
> }
>
> -static int check_gsc_manifest(struct intel_gt *gt,
> - const struct firmware *fw,
> - struct intel_uc_fw *uc_fw)
> -{
> - u32 *dw = (u32 *)fw->data;
> - u32 version_hi, version_lo;
> - size_t min_size;
> -
> - /* Check the size of the blob before examining buffer contents */
> - min_size = sizeof(u32) * (HUC_GSC_VERSION_LO_DW + 1);
> - if (unlikely(fw->size < min_size)) {
> - gt_warn(gt, "%s firmware %s: invalid size: %zu < %zu\n",
> - intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
> - fw->size, min_size);
> - return -ENODATA;
> - }
> -
> - version_hi = dw[HUC_GSC_VERSION_HI_DW];
> - version_lo = dw[HUC_GSC_VERSION_LO_DW];
> -
> - uc_fw->file_selected.ver.major = FIELD_GET(HUC_GSC_MAJOR_VER_HI_MASK, version_hi);
> - uc_fw->file_selected.ver.minor = FIELD_GET(HUC_GSC_MINOR_VER_HI_MASK, version_hi);
> - uc_fw->file_selected.ver.patch = FIELD_GET(HUC_GSC_PATCH_VER_LO_MASK, version_lo);
> -
> - return 0;
> -}
> -
> static void uc_unpack_css_version(struct intel_uc_fw_ver *ver, u32 css_value)
> {
> /* Get version numbers from the CSS header */
> @@ -631,22 +604,22 @@ static void guc_read_css_info(struct intel_uc_fw *uc_fw, struct uc_css_header *c
> uc_fw->private_data_size = css->private_data_size;
> }
>
> -static int check_ccs_header(struct intel_gt *gt,
> - const struct firmware *fw,
> - struct intel_uc_fw *uc_fw)
> +static int __check_ccs_header(struct intel_gt *gt,
> + const void *fw_data, size_t fw_size,
> + struct intel_uc_fw *uc_fw)
> {
> struct uc_css_header *css;
> size_t size;
>
> /* Check the size of the blob before examining buffer contents */
> - if (unlikely(fw->size < sizeof(struct uc_css_header))) {
> + if (unlikely(fw_size < sizeof(struct uc_css_header))) {
> gt_warn(gt, "%s firmware %s: invalid size: %zu < %zu\n",
> intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
> - fw->size, sizeof(struct uc_css_header));
> + fw_size, sizeof(struct uc_css_header));
> return -ENODATA;
> }
>
> - css = (struct uc_css_header *)fw->data;
> + css = (struct uc_css_header *)fw_data;
>
> /* Check integrity of size values inside CSS header */
> size = (css->header_size_dw - css->key_size_dw - css->modulus_size_dw -
> @@ -654,7 +627,7 @@ static int check_ccs_header(struct intel_gt *gt,
> if (unlikely(size != sizeof(struct uc_css_header))) {
> gt_warn(gt, "%s firmware %s: unexpected header size: %zu != %zu\n",
> intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
> - fw->size, sizeof(struct uc_css_header));
> + fw_size, sizeof(struct uc_css_header));
> return -EPROTO;
> }
>
> @@ -666,10 +639,10 @@ static int check_ccs_header(struct intel_gt *gt,
>
> /* At least, it should have header, uCode and RSA. Size of all three. */
> size = sizeof(struct uc_css_header) + uc_fw->ucode_size + uc_fw->rsa_size;
> - if (unlikely(fw->size < size)) {
> + if (unlikely(fw_size < size)) {
> gt_warn(gt, "%s firmware %s: invalid size: %zu < %zu\n",
> intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
> - fw->size, size);
> + fw_size, size);
> return -ENOEXEC;
> }
>
> @@ -690,6 +663,32 @@ static int check_ccs_header(struct intel_gt *gt,
> return 0;
> }
>
> +static int check_gsc_manifest(struct intel_gt *gt,
> + const struct firmware *fw,
> + struct intel_uc_fw *uc_fw)
> +{
> + if (uc_fw->type != INTEL_UC_FW_TYPE_HUC) {
> + gt_err(gt, "trying to GSC-parse a non-HuC binary");
> + return -EINVAL;
> + }
> +
> + intel_huc_fw_get_binary_info(uc_fw, fw->data, fw->size);
> +
> + if (uc_fw->dma_start_offset) {
> + u32 delta = uc_fw->dma_start_offset;
> + __check_ccs_header(gt, fw->data + delta, fw->size - delta, uc_fw);
> + }
> +
> + return 0;
> +}
> +
> +static int check_ccs_header(struct intel_gt *gt,
> + const struct firmware *fw,
> + struct intel_uc_fw *uc_fw)
> +{
> + return __check_ccs_header(gt, fw->data, fw->size, uc_fw);
> +}
> +
> static bool is_ver_8bit(struct intel_uc_fw_ver *ver)
> {
> return ver->major < 0xFF && ver->minor < 0xFF && ver->patch < 0xFF;
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h
> index 2be9470eb712..b3daba9526eb 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h
> @@ -118,6 +118,8 @@ struct intel_uc_fw {
> u32 ucode_size;
> u32 private_data_size;
>
> + u32 dma_start_offset;
> +
> bool loaded_via_gsc;
> };
>
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h
> index 646fa8aa6cf1..7fe405126249 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw_abi.h
> @@ -84,10 +84,4 @@ struct uc_css_header {
> } __packed;
> static_assert(sizeof(struct uc_css_header) == 128);
>
> -#define HUC_GSC_VERSION_HI_DW 44
> -#define HUC_GSC_MAJOR_VER_HI_MASK (0xFF << 0)
> -#define HUC_GSC_MINOR_VER_HI_MASK (0xFF << 16)
> -#define HUC_GSC_VERSION_LO_DW 45
> -#define HUC_GSC_PATCH_VER_LO_MASK (0xFF << 0)
> -
> #endif /* _INTEL_UC_FW_ABI_H */
More information about the Intel-gfx
mailing list