Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe/huc: Extract version and binary offset from new HuC headers

The GSC-enabled HuC binary starts with a GSC header, which is followed
by the legacy-style CSS header and the binary itself. We can parse the
GSC headers to find the HuC version and the location of the binary to
be used for the DMA transfer.

The parsing function has been designed to be re-used for the GSC binary,
so the entry names are external parameters (because the GSC uses
different ones) and the CSS entry is optional (because the GSC doesn't
have it).

v2: move new code to uc_fw.c, better comments and error checking, split
old code move to separate patch (Lucas), move headers and
documentation to uc_fw_abi.h.

v3: use 2 separate loops, rework marker check (Lucas)

Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: John Harrison <John.C.Harrison@Intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

authored by

Daniele Ceraolo Spurio and committed by
Rodrigo Vivi
484ecffa a9a95523

+244 -3
+3
Documentation/gpu/xe/xe_firmware.rst
··· 10 10 .. kernel-doc:: drivers/gpu/drm/xe/xe_uc_fw_abi.h 11 11 :doc: CSS-based Firmware Layout 12 12 13 + .. kernel-doc:: drivers/gpu/drm/xe/xe_uc_fw_abi.h 14 + :doc: GSC-based Firmware Layout 15 + 13 16 Write Once Protected Content Memory (WOPCM) Layout 14 17 ================================================== 15 18
+118 -2
drivers/gpu/drm/xe/xe_uc_fw.c
··· 402 402 return 0; 403 403 } 404 404 405 + static bool is_cpd_header(const void *data) 406 + { 407 + const u32 *marker = data; 408 + 409 + return *marker == GSC_CPD_HEADER_MARKER; 410 + } 411 + 412 + static u32 entry_offset(const struct gsc_cpd_header_v2 *header, const char *name) 413 + { 414 + const struct gsc_cpd_entry *entry; 415 + int i; 416 + 417 + entry = (void *)header + header->header_length; 418 + 419 + for (i = 0; i < header->num_of_entries; i++, entry++) 420 + if (strcmp(entry->name, name) == 0) 421 + return entry->offset & GSC_CPD_ENTRY_OFFSET_MASK; 422 + 423 + return 0; 424 + } 425 + 426 + /* Refer to the "GSC-based Firmware Layout" documentation entry for details */ 427 + static int parse_cpd_header(struct xe_uc_fw *uc_fw, const void *data, size_t size, 428 + const char *manifest_entry, const char *css_entry) 429 + { 430 + struct xe_gt *gt = uc_fw_to_gt(uc_fw); 431 + struct xe_device *xe = gt_to_xe(gt); 432 + const struct gsc_cpd_header_v2 *header = data; 433 + const struct gsc_manifest_header *manifest; 434 + size_t min_size = sizeof(*header); 435 + u32 offset; 436 + 437 + /* manifest_entry is mandatory, css_entry is optional */ 438 + xe_assert(xe, manifest_entry); 439 + 440 + if (size < min_size || !is_cpd_header(header)) 441 + return -ENOENT; 442 + 443 + if (header->header_length < sizeof(struct gsc_cpd_header_v2)) { 444 + xe_gt_err(gt, "invalid CPD header length %u!\n", header->header_length); 445 + return -EINVAL; 446 + } 447 + 448 + min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries; 449 + if (size < min_size) { 450 + xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size); 451 + return -ENODATA; 452 + } 453 + 454 + /* Look for the manifest first */ 455 + offset = entry_offset(header, manifest_entry); 456 + if (!offset) { 457 + xe_gt_err(gt, "Failed to find %s manifest!\n", 458 + xe_uc_fw_type_repr(uc_fw->type)); 459 + return -ENODATA; 460 + } 461 + 462 + min_size = offset + sizeof(struct gsc_manifest_header); 463 + if (size < min_size) { 464 + xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size); 465 + return -ENODATA; 466 + } 467 + 468 + manifest = data + offset; 469 + 470 + uc_fw->major_ver_found = manifest->fw_version.major; 471 + uc_fw->minor_ver_found = manifest->fw_version.minor; 472 + uc_fw->patch_ver_found = manifest->fw_version.hotfix; 473 + 474 + /* then optionally look for the css header */ 475 + if (css_entry) { 476 + int ret; 477 + 478 + /* 479 + * This section does not contain a CSS entry on DG2. We 480 + * don't support DG2 HuC right now, so no need to handle 481 + * it, just add a reminder in case that changes. 482 + */ 483 + xe_assert(xe, xe->info.platform != XE_DG2); 484 + 485 + offset = entry_offset(header, css_entry); 486 + 487 + /* the CSS header parser will check that the CSS header fits */ 488 + if (offset > size) { 489 + xe_gt_err(gt, "FW too small! %zu < %u\n", size, offset); 490 + return -ENODATA; 491 + } 492 + 493 + ret = parse_css_header(uc_fw, data + offset, size - offset); 494 + if (ret) 495 + return ret; 496 + 497 + uc_fw->css_offset = offset; 498 + } 499 + 500 + return 0; 501 + } 502 + 405 503 static int parse_headers(struct xe_uc_fw *uc_fw, const struct firmware *fw) 406 504 { 407 - return parse_css_header(uc_fw, fw->data, fw->size); 505 + int ret; 506 + 507 + /* 508 + * All GuC releases and older HuC ones use CSS headers, while newer HuC 509 + * releases use GSC CPD headers. 510 + */ 511 + switch (uc_fw->type) { 512 + case XE_UC_FW_TYPE_HUC: 513 + ret = parse_cpd_header(uc_fw, fw->data, fw->size, "HUCP.man", "huc_fw"); 514 + if (!ret || ret != -ENOENT) 515 + return ret; 516 + fallthrough; 517 + case XE_UC_FW_TYPE_GUC: 518 + return parse_css_header(uc_fw, fw->data, fw->size); 519 + default: 520 + return -EINVAL; 521 + } 522 + 523 + return 0; 408 524 } 409 525 410 526 int xe_uc_fw_init(struct xe_uc_fw *uc_fw) ··· 626 510 xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 627 511 628 512 /* Set the source address for the uCode */ 629 - src_offset = uc_fw_ggtt_offset(uc_fw); 513 + src_offset = uc_fw_ggtt_offset(uc_fw) + uc_fw->css_offset; 630 514 xe_mmio_write32(gt, DMA_ADDR_0_LOW, lower_32_bits(src_offset)); 631 515 xe_mmio_write32(gt, DMA_ADDR_0_HIGH, upper_32_bits(src_offset)); 632 516
+1 -1
drivers/gpu/drm/xe/xe_uc_fw.h
··· 21 21 22 22 static inline u32 xe_uc_fw_rsa_offset(struct xe_uc_fw *uc_fw) 23 23 { 24 - return sizeof(struct uc_css_header) + uc_fw->ucode_size; 24 + return sizeof(struct uc_css_header) + uc_fw->ucode_size + uc_fw->css_offset; 25 25 } 26 26 27 27 static inline void xe_uc_fw_change_status(struct xe_uc_fw *uc_fw,
+120
drivers/gpu/drm/xe/xe_uc_fw_abi.h
··· 85 85 } __packed; 86 86 static_assert(sizeof(struct uc_css_header) == 128); 87 87 88 + /** 89 + * DOC: GSC-based Firmware Layout 90 + * 91 + * The GSC-based firmware structure is used for GSC releases on all platforms 92 + * and for HuC releases starting from DG2/MTL. Older HuC releases use the 93 + * CSS-based layout instead. Differently from the CSS headers, the GSC headers 94 + * uses a directory + entries structure (i.e., there is array of addresses 95 + * pointing to specific header extensions identified by a name). Although the 96 + * header structures are the same, some of the entries are specific to GSC while 97 + * others are specific to HuC. The manifest header entry, which includes basic 98 + * information about the binary (like the version) is always present, but it is 99 + * named differently based on the binary type. 100 + * 101 + * The HuC binary starts with a Code Partition Directory (CPD) header. The 102 + * entries we're interested in for use in the driver are: 103 + * 104 + * 1. "HUCP.man": points to the manifest header for the HuC. 105 + * 2. "huc_fw": points to the FW code. On platforms that support load via DMA 106 + * and 2-step HuC authentication (i.e. MTL+) this is a full CSS-based binary, 107 + * while if the GSC is the one doing the load (which only happens on DG2) 108 + * this section only contains the uCode. 109 + * 110 + * The GSC-based HuC firmware layout looks like this:: 111 + * 112 + * +================================================+ 113 + * | CPD Header | 114 + * +================================================+ 115 + * | CPD entries[] | 116 + * | entry1 | 117 + * | ... | 118 + * | entryX | 119 + * | "HUCP.man" | 120 + * | ... | 121 + * | offset >----------------------------|------o 122 + * | ... | | 123 + * | entryY | | 124 + * | "huc_fw" | | 125 + * | ... | | 126 + * | offset >----------------------------|----------o 127 + * +================================================+ | | 128 + * | | 129 + * +================================================+ | | 130 + * | Manifest Header |<-----o | 131 + * | ... | | 132 + * | FW version | | 133 + * | ... | | 134 + * +================================================+ | 135 + * | 136 + * +================================================+ | 137 + * | FW binary |<---------o 138 + * | CSS (MTL+ only) | 139 + * | uCode | 140 + * | RSA Key (MTL+ only) | 141 + * | ... | 142 + * +================================================+ 143 + */ 144 + 145 + struct gsc_version { 146 + u16 major; 147 + u16 minor; 148 + u16 hotfix; 149 + u16 build; 150 + } __packed; 151 + 152 + /* Code partition directory (CPD) structures */ 153 + struct gsc_cpd_header_v2 { 154 + u32 header_marker; 155 + #define GSC_CPD_HEADER_MARKER 0x44504324 156 + 157 + u32 num_of_entries; 158 + u8 header_version; 159 + u8 entry_version; 160 + u8 header_length; /* in bytes */ 161 + u8 flags; 162 + u32 partition_name; 163 + u32 crc32; 164 + } __packed; 165 + 166 + struct gsc_cpd_entry { 167 + u8 name[12]; 168 + 169 + /* 170 + * Bits 0-24: offset from the beginning of the code partition 171 + * Bit 25: huffman compressed 172 + * Bits 26-31: reserved 173 + */ 174 + u32 offset; 175 + #define GSC_CPD_ENTRY_OFFSET_MASK GENMASK(24, 0) 176 + #define GSC_CPD_ENTRY_HUFFMAN_COMP BIT(25) 177 + 178 + /* 179 + * Module/Item length, in bytes. For Huffman-compressed modules, this 180 + * refers to the uncompressed size. For software-compressed modules, 181 + * this refers to the compressed size. 182 + */ 183 + u32 length; 184 + 185 + u8 reserved[4]; 186 + } __packed; 187 + 188 + struct gsc_manifest_header { 189 + u32 header_type; /* 0x4 for manifest type */ 190 + u32 header_length; /* in dwords */ 191 + u32 header_version; 192 + u32 flags; 193 + u32 vendor; 194 + u32 date; 195 + u32 size; /* In dwords, size of entire manifest (header + extensions) */ 196 + u32 header_id; 197 + u32 internal_data; 198 + struct gsc_version fw_version; 199 + u32 security_version; 200 + struct gsc_version meu_kit_version; 201 + u32 meu_manifest_version; 202 + u8 general_data[4]; 203 + u8 reserved3[56]; 204 + u32 modulus_size; /* in dwords */ 205 + u32 exponent_size; /* in dwords */ 206 + } __packed; 207 + 88 208 #endif
+2
drivers/gpu/drm/xe/xe_uc_fw_types.h
··· 113 113 u32 rsa_size; 114 114 /** @ucode_size: micro kernel size */ 115 115 u32 ucode_size; 116 + /** @css_offset: offset within the blob at which the CSS is located */ 117 + u32 css_offset; 116 118 117 119 /** @private_data_size: size of private data found in uC css header */ 118 120 u32 private_data_size;