1443 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright © 2016-2019 Intel Corporation4 */5 6#include <linux/bitfield.h>7#include <linux/firmware.h>8#include <linux/highmem.h>9 10#include <drm/drm_cache.h>11#include <drm/drm_print.h>12 13#include "gem/i915_gem_lmem.h"14#include "gt/intel_gt.h"15#include "gt/intel_gt_print.h"16#include "intel_gsc_binary_headers.h"17#include "intel_gsc_fw.h"18#include "intel_uc_fw.h"19#include "intel_uc_fw_abi.h"20#include "i915_drv.h"21#include "i915_reg.h"22 23#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)24#define UNEXPECTED gt_probe_error25#else26#define UNEXPECTED gt_notice27#endif28 29static inline struct intel_gt *30____uc_fw_to_gt(struct intel_uc_fw *uc_fw, enum intel_uc_fw_type type)31{32 GEM_BUG_ON(type >= INTEL_UC_FW_NUM_TYPES);33 34 switch (type) {35 case INTEL_UC_FW_TYPE_GUC:36 return container_of(uc_fw, struct intel_gt, uc.guc.fw);37 case INTEL_UC_FW_TYPE_HUC:38 return container_of(uc_fw, struct intel_gt, uc.huc.fw);39 case INTEL_UC_FW_TYPE_GSC:40 return container_of(uc_fw, struct intel_gt, uc.gsc.fw);41 }42 43 return NULL;44}45 46static inline struct intel_gt *__uc_fw_to_gt(struct intel_uc_fw *uc_fw)47{48 GEM_BUG_ON(uc_fw->status == INTEL_UC_FIRMWARE_UNINITIALIZED);49 return ____uc_fw_to_gt(uc_fw, uc_fw->type);50}51 52#ifdef CONFIG_DRM_I915_DEBUG_GUC53void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw,54 enum intel_uc_fw_status status)55{56 uc_fw->__status = status;57 gt_dbg(__uc_fw_to_gt(uc_fw), "%s firmware -> %s\n",58 intel_uc_fw_type_repr(uc_fw->type),59 status == INTEL_UC_FIRMWARE_SELECTED ?60 uc_fw->file_selected.path : intel_uc_fw_status_repr(status));61}62#endif63 64/*65 * List of required GuC and HuC binaries per-platform.66 * Must be ordered based on platform + revid, from newer to older.67 *68 * Note that RKL and ADL-S have the same GuC/HuC device ID's and use the same69 * firmware as TGL.70 *71 * Version numbers:72 * Originally, the driver required an exact match major/minor/patch furmware73 * file and only supported that one version for any given platform. However,74 * the new direction from upstream is to be backwards compatible with all75 * prior releases and to be as flexible as possible as to what firmware is76 * loaded.77 *78 * For GuC, the major version number signifies a backwards breaking API change.79 * So, new format GuC firmware files are labelled by their major version only.80 * For HuC, there is no KMD interaction, hence no version matching requirement.81 * So, new format HuC firmware files have no version number at all.82 *83 * All of which means that the table below must keep all old format files with84 * full three point version number. But newer files have reduced requirements.85 * Having said that, the driver still needs to track the minor version number86 * for GuC at least. As it is useful to report to the user that they are not87 * running with a recent enough version for all KMD supported features,88 * security fixes, etc. to be enabled.89 */90#define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \91 fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 12, 1)) \92 fw_def(DG2, 0, guc_maj(dg2, 70, 12, 1)) \93 fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 12, 1)) \94 fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \95 fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \96 fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 12, 1)) \97 fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \98 fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \99 fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \100 fw_def(ROCKETLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \101 fw_def(TIGERLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \102 fw_def(JASPERLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \103 fw_def(ELKHARTLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \104 fw_def(ICELAKE, 0, guc_mmp(icl, 70, 1, 1)) \105 fw_def(COMETLAKE, 5, guc_mmp(cml, 70, 1, 1)) \106 fw_def(COMETLAKE, 0, guc_mmp(kbl, 70, 1, 1)) \107 fw_def(COFFEELAKE, 0, guc_mmp(kbl, 70, 1, 1)) \108 fw_def(GEMINILAKE, 0, guc_mmp(glk, 70, 1, 1)) \109 fw_def(KABYLAKE, 0, guc_mmp(kbl, 70, 1, 1)) \110 fw_def(BROXTON, 0, guc_mmp(bxt, 70, 1, 1)) \111 fw_def(SKYLAKE, 0, guc_mmp(skl, 70, 1, 1))112 113#define INTEL_HUC_FIRMWARE_DEFS(fw_def, huc_raw, huc_mmp, huc_gsc) \114 fw_def(METEORLAKE, 0, huc_gsc(mtl)) \115 fw_def(DG2, 0, huc_gsc(dg2)) \116 fw_def(ALDERLAKE_P, 0, huc_raw(tgl)) \117 fw_def(ALDERLAKE_P, 0, huc_mmp(tgl, 7, 9, 3)) \118 fw_def(ALDERLAKE_S, 0, huc_raw(tgl)) \119 fw_def(ALDERLAKE_S, 0, huc_mmp(tgl, 7, 9, 3)) \120 fw_def(DG1, 0, huc_raw(dg1)) \121 fw_def(ROCKETLAKE, 0, huc_mmp(tgl, 7, 9, 3)) \122 fw_def(TIGERLAKE, 0, huc_mmp(tgl, 7, 9, 3)) \123 fw_def(JASPERLAKE, 0, huc_mmp(ehl, 9, 0, 0)) \124 fw_def(ELKHARTLAKE, 0, huc_mmp(ehl, 9, 0, 0)) \125 fw_def(ICELAKE, 0, huc_mmp(icl, 9, 0, 0)) \126 fw_def(COMETLAKE, 5, huc_mmp(cml, 4, 0, 0)) \127 fw_def(COMETLAKE, 0, huc_mmp(kbl, 4, 0, 0)) \128 fw_def(COFFEELAKE, 0, huc_mmp(kbl, 4, 0, 0)) \129 fw_def(GEMINILAKE, 0, huc_mmp(glk, 4, 0, 0)) \130 fw_def(KABYLAKE, 0, huc_mmp(kbl, 4, 0, 0)) \131 fw_def(BROXTON, 0, huc_mmp(bxt, 2, 0, 0)) \132 fw_def(SKYLAKE, 0, huc_mmp(skl, 2, 0, 0))133 134/*135 * The GSC FW has multiple version (see intel_gsc_uc.h for details); since what136 * we care about is the interface, we use the compatibility version in the137 * binary names.138 * Same as with the GuC, a major version bump indicate a139 * backward-incompatible change, while a minor version bump indicates a140 * backward-compatible one, so we use only the former in the file name.141 */142#define INTEL_GSC_FIRMWARE_DEFS(fw_def, gsc_def) \143 fw_def(METEORLAKE, 0, gsc_def(mtl, 1, 0))144 145/*146 * Set of macros for producing a list of filenames from the above table.147 */148#define __MAKE_UC_FW_PATH_BLANK(prefix_, name_) \149 "i915/" \150 __stringify(prefix_) "_" name_ ".bin"151 152#define __MAKE_UC_FW_PATH_MAJOR(prefix_, name_, major_) \153 "i915/" \154 __stringify(prefix_) "_" name_ "_" \155 __stringify(major_) ".bin"156 157#define __MAKE_UC_FW_PATH_MMP(prefix_, name_, major_, minor_, patch_) \158 "i915/" \159 __stringify(prefix_) "_" name_ "_" \160 __stringify(major_) "." \161 __stringify(minor_) "." \162 __stringify(patch_) ".bin"163 164/* Minor for internal driver use, not part of file name */165#define MAKE_GUC_FW_PATH_MAJOR(prefix_, major_, minor_, patch_) \166 __MAKE_UC_FW_PATH_MAJOR(prefix_, "guc", major_)167 168#define MAKE_GUC_FW_PATH_MMP(prefix_, major_, minor_, patch_) \169 __MAKE_UC_FW_PATH_MMP(prefix_, "guc", major_, minor_, patch_)170 171#define MAKE_HUC_FW_PATH_BLANK(prefix_) \172 __MAKE_UC_FW_PATH_BLANK(prefix_, "huc")173 174#define MAKE_HUC_FW_PATH_GSC(prefix_) \175 __MAKE_UC_FW_PATH_BLANK(prefix_, "huc_gsc")176 177#define MAKE_HUC_FW_PATH_MMP(prefix_, major_, minor_, patch_) \178 __MAKE_UC_FW_PATH_MMP(prefix_, "huc", major_, minor_, patch_)179 180#define MAKE_GSC_FW_PATH(prefix_, major_, minor_) \181 __MAKE_UC_FW_PATH_MAJOR(prefix_, "gsc", major_)182 183/*184 * All blobs need to be declared via MODULE_FIRMWARE().185 * This first expansion of the table macros is solely to provide186 * that declaration.187 */188#define INTEL_UC_MODULE_FW(platform_, revid_, uc_) \189 MODULE_FIRMWARE(uc_);190 191INTEL_GUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH_MAJOR, MAKE_GUC_FW_PATH_MMP)192INTEL_HUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_HUC_FW_PATH_BLANK, MAKE_HUC_FW_PATH_MMP, MAKE_HUC_FW_PATH_GSC)193INTEL_GSC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GSC_FW_PATH)194 195/*196 * The next expansion of the table macros (in __uc_fw_auto_select below) provides197 * actual data structures with both the filename and the version information.198 * These structure arrays are then iterated over to the list of suitable files199 * for the current platform and to then attempt to load those files, in the order200 * listed, until one is successfully found.201 */202struct __packed uc_fw_blob {203 const char *path;204 bool legacy;205 u8 major;206 u8 minor;207 u8 patch;208 bool has_gsc_headers;209};210 211#define UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \212 .major = major_, \213 .minor = minor_, \214 .patch = patch_, \215 .path = path_,216 217#define UC_FW_BLOB_NEW(major_, minor_, patch_, gsc_, path_) \218 { UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \219 .legacy = false, .has_gsc_headers = gsc_ }220 221#define UC_FW_BLOB_OLD(major_, minor_, patch_, path_) \222 { UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \223 .legacy = true }224 225#define GUC_FW_BLOB(prefix_, major_, minor_, patch_) \226 UC_FW_BLOB_NEW(major_, minor_, patch_, false, \227 MAKE_GUC_FW_PATH_MAJOR(prefix_, major_, minor_, patch_))228 229#define GUC_FW_BLOB_MMP(prefix_, major_, minor_, patch_) \230 UC_FW_BLOB_OLD(major_, minor_, patch_, \231 MAKE_GUC_FW_PATH_MMP(prefix_, major_, minor_, patch_))232 233#define HUC_FW_BLOB(prefix_) \234 UC_FW_BLOB_NEW(0, 0, 0, false, MAKE_HUC_FW_PATH_BLANK(prefix_))235 236#define HUC_FW_BLOB_MMP(prefix_, major_, minor_, patch_) \237 UC_FW_BLOB_OLD(major_, minor_, patch_, \238 MAKE_HUC_FW_PATH_MMP(prefix_, major_, minor_, patch_))239 240#define HUC_FW_BLOB_GSC(prefix_) \241 UC_FW_BLOB_NEW(0, 0, 0, true, MAKE_HUC_FW_PATH_GSC(prefix_))242 243#define GSC_FW_BLOB(prefix_, major_, minor_) \244 UC_FW_BLOB_NEW(major_, minor_, 0, true, \245 MAKE_GSC_FW_PATH(prefix_, major_, minor_))246 247struct __packed uc_fw_platform_requirement {248 enum intel_platform p;249 u8 rev; /* first platform rev using this FW */250 const struct uc_fw_blob blob;251};252 253#define MAKE_FW_LIST(platform_, revid_, uc_) \254{ \255 .p = INTEL_##platform_, \256 .rev = revid_, \257 .blob = uc_, \258},259 260struct fw_blobs_by_type {261 const struct uc_fw_platform_requirement *blobs;262 u32 count;263};264 265static const struct uc_fw_platform_requirement blobs_guc[] = {266 INTEL_GUC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB, GUC_FW_BLOB_MMP)267};268 269static const struct uc_fw_platform_requirement blobs_huc[] = {270 INTEL_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB, HUC_FW_BLOB_MMP, HUC_FW_BLOB_GSC)271};272 273static const struct uc_fw_platform_requirement blobs_gsc[] = {274 INTEL_GSC_FIRMWARE_DEFS(MAKE_FW_LIST, GSC_FW_BLOB)275};276 277static const struct fw_blobs_by_type blobs_all[INTEL_UC_FW_NUM_TYPES] = {278 [INTEL_UC_FW_TYPE_GUC] = { blobs_guc, ARRAY_SIZE(blobs_guc) },279 [INTEL_UC_FW_TYPE_HUC] = { blobs_huc, ARRAY_SIZE(blobs_huc) },280 [INTEL_UC_FW_TYPE_GSC] = { blobs_gsc, ARRAY_SIZE(blobs_gsc) },281};282 283static void284__uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw)285{286 const struct uc_fw_platform_requirement *fw_blobs;287 enum intel_platform p = INTEL_INFO(i915)->platform;288 u32 fw_count;289 u8 rev = INTEL_REVID(i915);290 int i;291 bool found;292 293 /*294 * The only difference between the ADL GuC FWs is the HWConfig support.295 * ADL-N does not support HWConfig, so we should use the same binary as296 * ADL-S, otherwise the GuC might attempt to fetch a config table that297 * does not exist.298 */299 if (IS_ALDERLAKE_P_N(i915))300 p = INTEL_ALDERLAKE_S;301 302 GEM_BUG_ON(uc_fw->type >= ARRAY_SIZE(blobs_all));303 fw_blobs = blobs_all[uc_fw->type].blobs;304 fw_count = blobs_all[uc_fw->type].count;305 306 found = false;307 for (i = 0; i < fw_count && p <= fw_blobs[i].p; i++) {308 const struct uc_fw_blob *blob = &fw_blobs[i].blob;309 310 if (p != fw_blobs[i].p)311 continue;312 313 if (rev < fw_blobs[i].rev)314 continue;315 316 if (uc_fw->file_selected.path) {317 /*318 * Continuing an earlier search after a found blob failed to load.319 * Once the previously chosen path has been found, clear it out320 * and let the search continue from there.321 */322 if (uc_fw->file_selected.path == blob->path)323 uc_fw->file_selected.path = NULL;324 325 continue;326 }327 328 uc_fw->file_selected.path = blob->path;329 uc_fw->file_wanted.path = blob->path;330 uc_fw->file_wanted.ver.major = blob->major;331 uc_fw->file_wanted.ver.minor = blob->minor;332 uc_fw->file_wanted.ver.patch = blob->patch;333 uc_fw->has_gsc_headers = blob->has_gsc_headers;334 found = true;335 break;336 }337 338 if (!found && uc_fw->file_selected.path) {339 /* Failed to find a match for the last attempt?! */340 uc_fw->file_selected.path = NULL;341 }342}343 344static bool validate_fw_table_type(struct drm_i915_private *i915, enum intel_uc_fw_type type)345{346 const struct uc_fw_platform_requirement *fw_blobs;347 u32 fw_count;348 int i, j;349 350 if (type >= ARRAY_SIZE(blobs_all)) {351 drm_err(&i915->drm, "No blob array for %s\n", intel_uc_fw_type_repr(type));352 return false;353 }354 355 fw_blobs = blobs_all[type].blobs;356 fw_count = blobs_all[type].count;357 358 if (!fw_count)359 return true;360 361 /* make sure the list is ordered as expected */362 for (i = 1; i < fw_count; i++) {363 /* Versionless file names must be unique per platform: */364 for (j = i + 1; j < fw_count; j++) {365 /* Same platform? */366 if (fw_blobs[i].p != fw_blobs[j].p)367 continue;368 369 if (fw_blobs[i].blob.path != fw_blobs[j].blob.path)370 continue;371 372 drm_err(&i915->drm, "Duplicate %s blobs: %s r%u %s%d.%d.%d [%s] matches %s%d.%d.%d [%s]\n",373 intel_uc_fw_type_repr(type),374 intel_platform_name(fw_blobs[j].p), fw_blobs[j].rev,375 fw_blobs[j].blob.legacy ? "L" : "v",376 fw_blobs[j].blob.major, fw_blobs[j].blob.minor,377 fw_blobs[j].blob.patch, fw_blobs[j].blob.path,378 fw_blobs[i].blob.legacy ? "L" : "v",379 fw_blobs[i].blob.major, fw_blobs[i].blob.minor,380 fw_blobs[i].blob.patch, fw_blobs[i].blob.path);381 }382 383 /* Next platform is good: */384 if (fw_blobs[i].p < fw_blobs[i - 1].p)385 continue;386 387 /* Next platform revision is good: */388 if (fw_blobs[i].p == fw_blobs[i - 1].p &&389 fw_blobs[i].rev < fw_blobs[i - 1].rev)390 continue;391 392 /* Platform/revision must be in order: */393 if (fw_blobs[i].p != fw_blobs[i - 1].p ||394 fw_blobs[i].rev != fw_blobs[i - 1].rev)395 goto bad;396 397 /* Next major version is good: */398 if (fw_blobs[i].blob.major < fw_blobs[i - 1].blob.major)399 continue;400 401 /* New must be before legacy: */402 if (!fw_blobs[i].blob.legacy && fw_blobs[i - 1].blob.legacy)403 goto bad;404 405 /* New to legacy also means 0.0 to X.Y (HuC), or X.0 to X.Y (GuC) */406 if (fw_blobs[i].blob.legacy && !fw_blobs[i - 1].blob.legacy) {407 if (!fw_blobs[i - 1].blob.major)408 continue;409 410 if (fw_blobs[i].blob.major == fw_blobs[i - 1].blob.major)411 continue;412 }413 414 /* Major versions must be in order: */415 if (fw_blobs[i].blob.major != fw_blobs[i - 1].blob.major)416 goto bad;417 418 /* Next minor version is good: */419 if (fw_blobs[i].blob.minor < fw_blobs[i - 1].blob.minor)420 continue;421 422 /* Minor versions must be in order: */423 if (fw_blobs[i].blob.minor != fw_blobs[i - 1].blob.minor)424 goto bad;425 426 /* Patch versions must be in order and unique: */427 if (fw_blobs[i].blob.patch < fw_blobs[i - 1].blob.patch)428 continue;429 430bad:431 drm_err(&i915->drm, "Invalid %s blob order: %s r%u %s%d.%d.%d comes before %s r%u %s%d.%d.%d\n",432 intel_uc_fw_type_repr(type),433 intel_platform_name(fw_blobs[i - 1].p), fw_blobs[i - 1].rev,434 fw_blobs[i - 1].blob.legacy ? "L" : "v",435 fw_blobs[i - 1].blob.major,436 fw_blobs[i - 1].blob.minor,437 fw_blobs[i - 1].blob.patch,438 intel_platform_name(fw_blobs[i].p), fw_blobs[i].rev,439 fw_blobs[i].blob.legacy ? "L" : "v",440 fw_blobs[i].blob.major,441 fw_blobs[i].blob.minor,442 fw_blobs[i].blob.patch);443 return false;444 }445 446 return true;447}448 449static const char *__override_guc_firmware_path(struct drm_i915_private *i915)450{451 if (i915->params.enable_guc & ENABLE_GUC_MASK)452 return i915->params.guc_firmware_path;453 return "";454}455 456static const char *__override_huc_firmware_path(struct drm_i915_private *i915)457{458 if (i915->params.enable_guc & ENABLE_GUC_LOAD_HUC)459 return i915->params.huc_firmware_path;460 return "";461}462 463static const char *__override_gsc_firmware_path(struct drm_i915_private *i915)464{465 return i915->params.gsc_firmware_path;466}467 468static void __uc_fw_user_override(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw)469{470 const char *path = NULL;471 472 switch (uc_fw->type) {473 case INTEL_UC_FW_TYPE_GUC:474 path = __override_guc_firmware_path(i915);475 break;476 case INTEL_UC_FW_TYPE_HUC:477 path = __override_huc_firmware_path(i915);478 break;479 case INTEL_UC_FW_TYPE_GSC:480 path = __override_gsc_firmware_path(i915);481 break;482 }483 484 if (unlikely(path)) {485 uc_fw->file_selected.path = path;486 uc_fw->user_overridden = true;487 }488}489 490void intel_uc_fw_version_from_gsc_manifest(struct intel_uc_fw_ver *ver,491 const void *data)492{493 const struct intel_gsc_manifest_header *manifest = data;494 495 ver->major = manifest->fw_version.major;496 ver->minor = manifest->fw_version.minor;497 ver->patch = manifest->fw_version.hotfix;498 ver->build = manifest->fw_version.build;499}500 501/**502 * intel_uc_fw_init_early - initialize the uC object and select the firmware503 * @uc_fw: uC firmware504 * @type: type of uC505 * @needs_ggtt_mapping: whether the FW needs to be GGTT mapped for loading506 *507 * Initialize the state of our uC object and relevant tracking and select the508 * firmware to fetch and load.509 */510void intel_uc_fw_init_early(struct intel_uc_fw *uc_fw,511 enum intel_uc_fw_type type,512 bool needs_ggtt_mapping)513{514 struct intel_gt *gt = ____uc_fw_to_gt(uc_fw, type);515 struct drm_i915_private *i915 = gt->i915;516 517 /*518 * we use FIRMWARE_UNINITIALIZED to detect checks against uc_fw->status519 * before we're looked at the HW caps to see if we have uc support520 */521 BUILD_BUG_ON(INTEL_UC_FIRMWARE_UNINITIALIZED);522 GEM_BUG_ON(uc_fw->status);523 GEM_BUG_ON(uc_fw->file_selected.path);524 525 uc_fw->type = type;526 uc_fw->needs_ggtt_mapping = needs_ggtt_mapping;527 528 if (HAS_GT_UC(i915)) {529 if (!validate_fw_table_type(i915, type)) {530 gt->uc.fw_table_invalid = true;531 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_NOT_SUPPORTED);532 return;533 }534 535 __uc_fw_auto_select(i915, uc_fw);536 __uc_fw_user_override(i915, uc_fw);537 }538 539 intel_uc_fw_change_status(uc_fw, uc_fw->file_selected.path ? *uc_fw->file_selected.path ?540 INTEL_UC_FIRMWARE_SELECTED :541 INTEL_UC_FIRMWARE_DISABLED :542 INTEL_UC_FIRMWARE_NOT_SUPPORTED);543}544 545static void __force_fw_fetch_failures(struct intel_uc_fw *uc_fw, int e)546{547 struct drm_i915_private *i915 = __uc_fw_to_gt(uc_fw)->i915;548 bool user = e == -EINVAL;549 550 if (i915_inject_probe_error(i915, e)) {551 /* non-existing blob */552 uc_fw->file_selected.path = "<invalid>";553 uc_fw->user_overridden = user;554 } else if (i915_inject_probe_error(i915, e)) {555 /* require next major version */556 uc_fw->file_wanted.ver.major += 1;557 uc_fw->file_wanted.ver.minor = 0;558 uc_fw->user_overridden = user;559 } else if (i915_inject_probe_error(i915, e)) {560 /* require next minor version */561 uc_fw->file_wanted.ver.minor += 1;562 uc_fw->user_overridden = user;563 } else if (uc_fw->file_wanted.ver.major &&564 i915_inject_probe_error(i915, e)) {565 /* require prev major version */566 uc_fw->file_wanted.ver.major -= 1;567 uc_fw->file_wanted.ver.minor = 0;568 uc_fw->user_overridden = user;569 } else if (uc_fw->file_wanted.ver.minor &&570 i915_inject_probe_error(i915, e)) {571 /* require prev minor version - hey, this should work! */572 uc_fw->file_wanted.ver.minor -= 1;573 uc_fw->user_overridden = user;574 } else if (user && i915_inject_probe_error(i915, e)) {575 /* officially unsupported platform */576 uc_fw->file_wanted.ver.major = 0;577 uc_fw->file_wanted.ver.minor = 0;578 uc_fw->user_overridden = true;579 }580}581 582static void uc_unpack_css_version(struct intel_uc_fw_ver *ver, u32 css_value)583{584 /* Get version numbers from the CSS header */585 ver->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css_value);586 ver->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css_value);587 ver->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css_value);588}589 590static void guc_read_css_info(struct intel_uc_fw *uc_fw, struct uc_css_header *css)591{592 struct intel_guc *guc = container_of(uc_fw, struct intel_guc, fw);593 594 /*595 * The GuC firmware includes an extra version number to specify the596 * submission API level. This allows submission code to work with597 * multiple GuC versions without having to know the absolute firmware598 * version number (there are likely to be multiple firmware releases599 * which all support the same submission API level).600 *601 * Note that the spec for the CSS header defines this version number602 * as 'vf_version' as it was originally intended for virtualisation.603 * However, it is applicable to native submission as well.604 *605 * Unfortunately, due to an oversight, this version number was only606 * exposed in the CSS header from v70.6.0.607 */608 if (uc_fw->file_selected.ver.major >= 70) {609 if (uc_fw->file_selected.ver.minor >= 6) {610 /* v70.6.0 adds CSS header support */611 uc_unpack_css_version(&guc->submission_version, css->vf_version);612 } else if (uc_fw->file_selected.ver.minor >= 3) {613 /* v70.3.0 introduced v1.1.0 */614 guc->submission_version.major = 1;615 guc->submission_version.minor = 1;616 guc->submission_version.patch = 0;617 } else {618 /* v70.0.0 introduced v1.0.0 */619 guc->submission_version.major = 1;620 guc->submission_version.minor = 0;621 guc->submission_version.patch = 0;622 }623 } else if (uc_fw->file_selected.ver.major >= 69) {624 /* v69.0.0 introduced v0.10.0 */625 guc->submission_version.major = 0;626 guc->submission_version.minor = 10;627 guc->submission_version.patch = 0;628 } else {629 /* Prior versions were v0.1.0 */630 guc->submission_version.major = 0;631 guc->submission_version.minor = 1;632 guc->submission_version.patch = 0;633 }634 635 uc_fw->private_data_size = css->private_data_size;636}637 638static int __check_ccs_header(struct intel_gt *gt,639 const void *fw_data, size_t fw_size,640 struct intel_uc_fw *uc_fw)641{642 struct uc_css_header *css;643 size_t size;644 645 /* Check the size of the blob before examining buffer contents */646 if (unlikely(fw_size < sizeof(struct uc_css_header))) {647 gt_warn(gt, "%s firmware %s: invalid size: %zu < %zu\n",648 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,649 fw_size, sizeof(struct uc_css_header));650 return -ENODATA;651 }652 653 css = (struct uc_css_header *)fw_data;654 655 /* Check integrity of size values inside CSS header */656 size = (css->header_size_dw - css->key_size_dw - css->modulus_size_dw -657 css->exponent_size_dw) * sizeof(u32);658 if (unlikely(size != sizeof(struct uc_css_header))) {659 gt_warn(gt, "%s firmware %s: unexpected header size: %zu != %zu\n",660 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,661 fw_size, sizeof(struct uc_css_header));662 return -EPROTO;663 }664 665 /* uCode size must calculated from other sizes */666 uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);667 668 /* now RSA */669 uc_fw->rsa_size = css->key_size_dw * sizeof(u32);670 671 /* At least, it should have header, uCode and RSA. Size of all three. */672 size = sizeof(struct uc_css_header) + uc_fw->ucode_size + uc_fw->rsa_size;673 if (unlikely(fw_size < size)) {674 gt_warn(gt, "%s firmware %s: invalid size: %zu < %zu\n",675 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,676 fw_size, size);677 return -ENOEXEC;678 }679 680 /* Sanity check whether this fw is not larger than whole WOPCM memory */681 size = __intel_uc_fw_get_upload_size(uc_fw);682 if (unlikely(size >= gt->wopcm.size)) {683 gt_warn(gt, "%s firmware %s: invalid size: %zu > %zu\n",684 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,685 size, (size_t)gt->wopcm.size);686 return -E2BIG;687 }688 689 uc_unpack_css_version(&uc_fw->file_selected.ver, css->sw_version);690 691 if (uc_fw->type == INTEL_UC_FW_TYPE_GUC)692 guc_read_css_info(uc_fw, css);693 694 return 0;695}696 697static int check_gsc_manifest(struct intel_gt *gt,698 const struct firmware *fw,699 struct intel_uc_fw *uc_fw)700{701 int ret;702 703 switch (uc_fw->type) {704 case INTEL_UC_FW_TYPE_HUC:705 ret = intel_huc_fw_get_binary_info(uc_fw, fw->data, fw->size);706 if (ret)707 return ret;708 break;709 case INTEL_UC_FW_TYPE_GSC:710 ret = intel_gsc_fw_get_binary_info(uc_fw, fw->data, fw->size);711 if (ret)712 return ret;713 break;714 default:715 MISSING_CASE(uc_fw->type);716 return -EINVAL;717 }718 719 if (uc_fw->dma_start_offset) {720 u32 delta = uc_fw->dma_start_offset;721 722 __check_ccs_header(gt, fw->data + delta, fw->size - delta, uc_fw);723 }724 725 return 0;726}727 728static int check_ccs_header(struct intel_gt *gt,729 const struct firmware *fw,730 struct intel_uc_fw *uc_fw)731{732 return __check_ccs_header(gt, fw->data, fw->size, uc_fw);733}734 735static bool is_ver_8bit(struct intel_uc_fw_ver *ver)736{737 return ver->major < 0xFF && ver->minor < 0xFF && ver->patch < 0xFF;738}739 740static int guc_check_version_range(struct intel_uc_fw *uc_fw)741{742 struct intel_guc *guc = container_of(uc_fw, struct intel_guc, fw);743 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);744 745 /*746 * GuC version number components are defined as being 8-bits.747 * The submission code relies on this to optimise version comparison748 * tests. So enforce the restriction here.749 */750 751 if (!is_ver_8bit(&uc_fw->file_selected.ver)) {752 gt_warn(gt, "%s firmware: invalid file version: 0x%02X:%02X:%02X\n",753 intel_uc_fw_type_repr(uc_fw->type),754 uc_fw->file_selected.ver.major,755 uc_fw->file_selected.ver.minor,756 uc_fw->file_selected.ver.patch);757 return -EINVAL;758 }759 760 if (!is_ver_8bit(&guc->submission_version)) {761 gt_warn(gt, "%s firmware: invalid submit version: 0x%02X:%02X:%02X\n",762 intel_uc_fw_type_repr(uc_fw->type),763 guc->submission_version.major,764 guc->submission_version.minor,765 guc->submission_version.patch);766 return -EINVAL;767 }768 769 return i915_inject_probe_error(gt->i915, -EINVAL);770}771 772static int check_fw_header(struct intel_gt *gt,773 const struct firmware *fw,774 struct intel_uc_fw *uc_fw)775{776 int err = 0;777 778 if (uc_fw->has_gsc_headers)779 err = check_gsc_manifest(gt, fw, uc_fw);780 else781 err = check_ccs_header(gt, fw, uc_fw);782 if (err)783 return err;784 785 return 0;786}787 788static int try_firmware_load(struct intel_uc_fw *uc_fw, const struct firmware **fw)789{790 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);791 struct device *dev = gt->i915->drm.dev;792 int err;793 794 err = firmware_request_nowarn(fw, uc_fw->file_selected.path, dev);795 796 if (err)797 return err;798 799 if (uc_fw->needs_ggtt_mapping && (*fw)->size > INTEL_UC_RSVD_GGTT_PER_FW) {800 gt_err(gt, "%s firmware %s: size (%zuKB) exceeds max supported size (%uKB)\n",801 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,802 (*fw)->size / SZ_1K, INTEL_UC_RSVD_GGTT_PER_FW / SZ_1K);803 804 /* try to find another blob to load */805 release_firmware(*fw);806 *fw = NULL;807 return -ENOENT;808 }809 810 return 0;811}812 813static int check_mtl_huc_guc_compatibility(struct intel_gt *gt,814 struct intel_uc_fw_file *huc_selected)815{816 struct intel_uc_fw_file *guc_selected = >_to_guc(gt)->fw.file_selected;817 struct intel_uc_fw_ver *huc_ver = &huc_selected->ver;818 struct intel_uc_fw_ver *guc_ver = &guc_selected->ver;819 bool new_huc, new_guc;820 821 /* we can only do this check after having fetched both GuC and HuC */822 GEM_BUG_ON(!huc_selected->path || !guc_selected->path);823 824 /*825 * Due to changes in the authentication flow for MTL, HuC 8.5.1 or newer826 * requires GuC 70.7.0 or newer. Older HuC binaries will instead require827 * GuC < 70.7.0.828 */829 new_huc = huc_ver->major > 8 ||830 (huc_ver->major == 8 && huc_ver->minor > 5) ||831 (huc_ver->major == 8 && huc_ver->minor == 5 && huc_ver->patch >= 1);832 833 new_guc = guc_ver->major > 70 ||834 (guc_ver->major == 70 && guc_ver->minor >= 7);835 836 if (new_huc != new_guc) {837 UNEXPECTED(gt, "HuC %u.%u.%u is incompatible with GuC %u.%u.%u\n",838 huc_ver->major, huc_ver->minor, huc_ver->patch,839 guc_ver->major, guc_ver->minor, guc_ver->patch);840 gt_info(gt, "MTL GuC 70.7.0+ and HuC 8.5.1+ don't work with older releases\n");841 return -ENOEXEC;842 }843 844 return 0;845}846 847int intel_uc_check_file_version(struct intel_uc_fw *uc_fw, bool *old_ver)848{849 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);850 struct intel_uc_fw_file *wanted = &uc_fw->file_wanted;851 struct intel_uc_fw_file *selected = &uc_fw->file_selected;852 int ret;853 854 /*855 * MTL has some compatibility issues with early GuC/HuC binaries856 * not working with newer ones. This is specific to MTL and we857 * don't expect it to extend to other platforms.858 */859 if (IS_METEORLAKE(gt->i915) && uc_fw->type == INTEL_UC_FW_TYPE_HUC) {860 ret = check_mtl_huc_guc_compatibility(gt, selected);861 if (ret)862 return ret;863 }864 865 if (!wanted->ver.major || !selected->ver.major)866 return 0;867 868 /* Check the file's major version was as it claimed */869 if (selected->ver.major != wanted->ver.major) {870 UNEXPECTED(gt, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",871 intel_uc_fw_type_repr(uc_fw->type), selected->path,872 selected->ver.major, selected->ver.minor,873 wanted->ver.major, wanted->ver.minor);874 if (!intel_uc_fw_is_overridden(uc_fw))875 return -ENOEXEC;876 } else if (old_ver) {877 if (selected->ver.minor < wanted->ver.minor)878 *old_ver = true;879 else if ((selected->ver.minor == wanted->ver.minor) &&880 (selected->ver.patch < wanted->ver.patch))881 *old_ver = true;882 }883 884 return 0;885}886 887/**888 * intel_uc_fw_fetch - fetch uC firmware889 * @uc_fw: uC firmware890 *891 * Fetch uC firmware into GEM obj.892 *893 * Return: 0 on success, a negative errno code on failure.894 */895int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw)896{897 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);898 struct drm_i915_private *i915 = gt->i915;899 struct intel_uc_fw_file file_ideal;900 struct drm_i915_gem_object *obj;901 const struct firmware *fw = NULL;902 bool old_ver = false;903 int err;904 905 GEM_BUG_ON(!gt->wopcm.size);906 GEM_BUG_ON(!intel_uc_fw_is_enabled(uc_fw));907 908 err = i915_inject_probe_error(i915, -ENXIO);909 if (err)910 goto fail;911 912 __force_fw_fetch_failures(uc_fw, -EINVAL);913 __force_fw_fetch_failures(uc_fw, -ESTALE);914 915 err = try_firmware_load(uc_fw, &fw);916 memcpy(&file_ideal, &uc_fw->file_wanted, sizeof(file_ideal));917 918 /* Any error is terminal if overriding. Don't bother searching for older versions */919 if (err && intel_uc_fw_is_overridden(uc_fw))920 goto fail;921 922 while (err == -ENOENT) {923 old_ver = true;924 925 __uc_fw_auto_select(i915, uc_fw);926 if (!uc_fw->file_selected.path) {927 /*928 * No more options! But set the path back to something929 * valid just in case it gets dereferenced.930 */931 uc_fw->file_selected.path = file_ideal.path;932 933 /* Also, preserve the version that was really wanted */934 memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted));935 break;936 }937 938 err = try_firmware_load(uc_fw, &fw);939 }940 941 if (err)942 goto fail;943 944 err = check_fw_header(gt, fw, uc_fw);945 if (err)946 goto fail;947 948 if (uc_fw->type == INTEL_UC_FW_TYPE_GUC) {949 err = guc_check_version_range(uc_fw);950 if (err)951 goto fail;952 }953 954 err = intel_uc_check_file_version(uc_fw, &old_ver);955 if (err)956 goto fail;957 958 if (old_ver && uc_fw->file_selected.ver.major) {959 /* Preserve the version that was really wanted */960 memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted));961 962 UNEXPECTED(gt, "%s firmware %s (%d.%d.%d) is recommended, but only %s (%d.%d.%d) was found\n",963 intel_uc_fw_type_repr(uc_fw->type),964 uc_fw->file_wanted.path,965 uc_fw->file_wanted.ver.major,966 uc_fw->file_wanted.ver.minor,967 uc_fw->file_wanted.ver.patch,968 uc_fw->file_selected.path,969 uc_fw->file_selected.ver.major,970 uc_fw->file_selected.ver.minor,971 uc_fw->file_selected.ver.patch);972 gt_info(gt, "Consider updating your linux-firmware pkg or downloading from %s\n",973 INTEL_UC_FIRMWARE_URL);974 }975 976 if (HAS_LMEM(i915)) {977 obj = i915_gem_object_create_lmem_from_data(i915, fw->data, fw->size);978 if (!IS_ERR(obj))979 obj->flags |= I915_BO_ALLOC_PM_EARLY;980 } else {981 obj = i915_gem_object_create_shmem_from_data(i915, fw->data, fw->size);982 }983 984 if (IS_ERR(obj)) {985 err = PTR_ERR(obj);986 goto fail;987 }988 989 uc_fw->obj = obj;990 uc_fw->size = fw->size;991 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_AVAILABLE);992 993 release_firmware(fw);994 return 0;995 996fail:997 intel_uc_fw_change_status(uc_fw, err == -ENOENT ?998 INTEL_UC_FIRMWARE_MISSING :999 INTEL_UC_FIRMWARE_ERROR);1000 1001 gt_probe_error(gt, "%s firmware %s: fetch failed %pe\n",1002 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path, ERR_PTR(err));1003 gt_info(gt, "%s firmware(s) can be downloaded from %s\n",1004 intel_uc_fw_type_repr(uc_fw->type), INTEL_UC_FIRMWARE_URL);1005 1006 release_firmware(fw); /* OK even if fw is NULL */1007 return err;1008}1009 1010static u32 uc_fw_ggtt_offset(struct intel_uc_fw *uc_fw)1011{1012 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);1013 struct i915_ggtt *ggtt = gt->ggtt;1014 struct drm_mm_node *node = &ggtt->uc_fw;1015 u32 offset = uc_fw->type * INTEL_UC_RSVD_GGTT_PER_FW;1016 1017 /*1018 * The media GT shares the GGTT with the root GT, which means that1019 * we need to use different offsets for the binaries on the media GT.1020 * To keep the math simple, we use 8MB for the root tile and 8MB for1021 * the media one. This will need to be updated if we ever have more1022 * than 1 media GT.1023 */1024 BUILD_BUG_ON(INTEL_UC_FW_NUM_TYPES * INTEL_UC_RSVD_GGTT_PER_FW > SZ_8M);1025 GEM_BUG_ON(gt->type == GT_MEDIA && gt->info.id > 1);1026 if (gt->type == GT_MEDIA)1027 offset += SZ_8M;1028 1029 GEM_BUG_ON(!drm_mm_node_allocated(node));1030 GEM_BUG_ON(upper_32_bits(node->start));1031 GEM_BUG_ON(upper_32_bits(node->start + node->size - 1));1032 GEM_BUG_ON(offset + uc_fw->obj->base.size > node->size);1033 GEM_BUG_ON(uc_fw->obj->base.size > INTEL_UC_RSVD_GGTT_PER_FW);1034 1035 return lower_32_bits(node->start + offset);1036}1037 1038static void uc_fw_bind_ggtt(struct intel_uc_fw *uc_fw)1039{1040 struct drm_i915_gem_object *obj = uc_fw->obj;1041 struct i915_ggtt *ggtt = __uc_fw_to_gt(uc_fw)->ggtt;1042 struct i915_vma_resource *vma_res = &uc_fw->vma_res;1043 u32 pte_flags = 0;1044 1045 if (!uc_fw->needs_ggtt_mapping)1046 return;1047 1048 vma_res->start = uc_fw_ggtt_offset(uc_fw);1049 vma_res->node_size = obj->base.size;1050 vma_res->bi.pages = obj->mm.pages;1051 1052 GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));1053 1054 /* uc_fw->obj cache domains were not controlled across suspend */1055 if (i915_gem_object_has_struct_page(obj))1056 drm_clflush_sg(vma_res->bi.pages);1057 1058 if (i915_gem_object_is_lmem(obj))1059 pte_flags |= PTE_LM;1060 1061 if (ggtt->vm.raw_insert_entries)1062 ggtt->vm.raw_insert_entries(&ggtt->vm, vma_res,1063 i915_gem_get_pat_index(ggtt->vm.i915,1064 I915_CACHE_NONE),1065 pte_flags);1066 else1067 ggtt->vm.insert_entries(&ggtt->vm, vma_res,1068 i915_gem_get_pat_index(ggtt->vm.i915,1069 I915_CACHE_NONE),1070 pte_flags);1071}1072 1073static void uc_fw_unbind_ggtt(struct intel_uc_fw *uc_fw)1074{1075 struct i915_ggtt *ggtt = __uc_fw_to_gt(uc_fw)->ggtt;1076 struct i915_vma_resource *vma_res = &uc_fw->vma_res;1077 1078 if (!vma_res->node_size)1079 return;1080 1081 ggtt->vm.clear_range(&ggtt->vm, vma_res->start, vma_res->node_size);1082}1083 1084static int uc_fw_xfer(struct intel_uc_fw *uc_fw, u32 dst_offset, u32 dma_flags)1085{1086 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);1087 struct intel_uncore *uncore = gt->uncore;1088 u64 offset;1089 int ret;1090 1091 ret = i915_inject_probe_error(gt->i915, -ETIMEDOUT);1092 if (ret)1093 return ret;1094 1095 intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL);1096 1097 /* Set the source address for the uCode */1098 offset = uc_fw->vma_res.start + uc_fw->dma_start_offset;1099 GEM_BUG_ON(upper_32_bits(offset) & 0xFFFF0000);1100 intel_uncore_write_fw(uncore, DMA_ADDR_0_LOW, lower_32_bits(offset));1101 intel_uncore_write_fw(uncore, DMA_ADDR_0_HIGH, upper_32_bits(offset));1102 1103 /* Set the DMA destination */1104 intel_uncore_write_fw(uncore, DMA_ADDR_1_LOW, dst_offset);1105 intel_uncore_write_fw(uncore, DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);1106 1107 /*1108 * Set the transfer size. The header plus uCode will be copied to WOPCM1109 * via DMA, excluding any other components1110 */1111 intel_uncore_write_fw(uncore, DMA_COPY_SIZE,1112 sizeof(struct uc_css_header) + uc_fw->ucode_size);1113 1114 /* Start the DMA */1115 intel_uncore_write_fw(uncore, DMA_CTRL,1116 _MASKED_BIT_ENABLE(dma_flags | START_DMA));1117 1118 /* Wait for DMA to finish */1119 ret = intel_wait_for_register_fw(uncore, DMA_CTRL, START_DMA, 0, 100);1120 if (ret)1121 gt_err(gt, "DMA for %s fw failed, DMA_CTRL=%u\n",1122 intel_uc_fw_type_repr(uc_fw->type),1123 intel_uncore_read_fw(uncore, DMA_CTRL));1124 1125 /* Disable the bits once DMA is over */1126 intel_uncore_write_fw(uncore, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags));1127 1128 intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL);1129 1130 return ret;1131}1132 1133int intel_uc_fw_mark_load_failed(struct intel_uc_fw *uc_fw, int err)1134{1135 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);1136 1137 GEM_BUG_ON(!intel_uc_fw_is_loadable(uc_fw));1138 1139 gt_probe_error(gt, "Failed to load %s firmware %s %pe\n",1140 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path, ERR_PTR(err));1141 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_LOAD_FAIL);1142 1143 return err;1144}1145 1146/**1147 * intel_uc_fw_upload - load uC firmware using custom loader1148 * @uc_fw: uC firmware1149 * @dst_offset: destination offset1150 * @dma_flags: flags for flags for dma ctrl1151 *1152 * Loads uC firmware and updates internal flags.1153 *1154 * Return: 0 on success, non-zero on failure.1155 */1156int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, u32 dst_offset, u32 dma_flags)1157{1158 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);1159 int err;1160 1161 /* make sure the status was cleared the last time we reset the uc */1162 GEM_BUG_ON(intel_uc_fw_is_loaded(uc_fw));1163 1164 err = i915_inject_probe_error(gt->i915, -ENOEXEC);1165 if (err)1166 return err;1167 1168 if (!intel_uc_fw_is_loadable(uc_fw))1169 return -ENOEXEC;1170 1171 /* Call custom loader */1172 err = uc_fw_xfer(uc_fw, dst_offset, dma_flags);1173 if (err)1174 goto fail;1175 1176 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_TRANSFERRED);1177 return 0;1178 1179fail:1180 return intel_uc_fw_mark_load_failed(uc_fw, err);1181}1182 1183static inline bool uc_fw_need_rsa_in_memory(struct intel_uc_fw *uc_fw)1184{1185 /*1186 * The HW reads the GuC RSA from memory if the key size is > 256 bytes,1187 * while it reads it from the 64 RSA registers if it is smaller.1188 * The HuC RSA is always read from memory.1189 */1190 return uc_fw->type == INTEL_UC_FW_TYPE_HUC || uc_fw->rsa_size > 256;1191}1192 1193static int uc_fw_rsa_data_create(struct intel_uc_fw *uc_fw)1194{1195 struct intel_gt *gt = __uc_fw_to_gt(uc_fw);1196 struct i915_vma *vma;1197 size_t copied;1198 void *vaddr;1199 int err;1200 1201 err = i915_inject_probe_error(gt->i915, -ENXIO);1202 if (err)1203 return err;1204 1205 if (!uc_fw_need_rsa_in_memory(uc_fw))1206 return 0;1207 1208 /*1209 * uC firmwares will sit above GUC_GGTT_TOP and will not map through1210 * GGTT. Unfortunately, this means that the GuC HW cannot perform the uC1211 * authentication from memory, as the RSA offset now falls within the1212 * GuC inaccessible range. We resort to perma-pinning an additional vma1213 * within the accessible range that only contains the RSA signature.1214 * The GuC HW can use this extra pinning to perform the authentication1215 * since its GGTT offset will be GuC accessible.1216 */1217 GEM_BUG_ON(uc_fw->rsa_size > PAGE_SIZE);1218 vma = intel_guc_allocate_vma(gt_to_guc(gt), PAGE_SIZE);1219 if (IS_ERR(vma))1220 return PTR_ERR(vma);1221 1222 vaddr = i915_gem_object_pin_map_unlocked(vma->obj,1223 intel_gt_coherent_map_type(gt, vma->obj, true));1224 if (IS_ERR(vaddr)) {1225 i915_vma_unpin_and_release(&vma, 0);1226 err = PTR_ERR(vaddr);1227 goto unpin_out;1228 }1229 1230 copied = intel_uc_fw_copy_rsa(uc_fw, vaddr, vma->size);1231 i915_gem_object_unpin_map(vma->obj);1232 1233 if (copied < uc_fw->rsa_size) {1234 err = -ENOMEM;1235 goto unpin_out;1236 }1237 1238 uc_fw->rsa_data = vma;1239 1240 return 0;1241 1242unpin_out:1243 i915_vma_unpin_and_release(&vma, 0);1244 return err;1245}1246 1247static void uc_fw_rsa_data_destroy(struct intel_uc_fw *uc_fw)1248{1249 i915_vma_unpin_and_release(&uc_fw->rsa_data, 0);1250}1251 1252int intel_uc_fw_init(struct intel_uc_fw *uc_fw)1253{1254 int err;1255 1256 /* this should happen before the load! */1257 GEM_BUG_ON(intel_uc_fw_is_loaded(uc_fw));1258 1259 if (!intel_uc_fw_is_available(uc_fw))1260 return -ENOEXEC;1261 1262 err = i915_gem_object_pin_pages_unlocked(uc_fw->obj);1263 if (err) {1264 gt_dbg(__uc_fw_to_gt(uc_fw), "%s fw pin-pages failed %pe\n",1265 intel_uc_fw_type_repr(uc_fw->type), ERR_PTR(err));1266 goto out;1267 }1268 1269 err = uc_fw_rsa_data_create(uc_fw);1270 if (err) {1271 gt_dbg(__uc_fw_to_gt(uc_fw), "%s fw rsa data creation failed %pe\n",1272 intel_uc_fw_type_repr(uc_fw->type), ERR_PTR(err));1273 goto out_unpin;1274 }1275 1276 uc_fw_bind_ggtt(uc_fw);1277 1278 return 0;1279 1280out_unpin:1281 i915_gem_object_unpin_pages(uc_fw->obj);1282out:1283 return err;1284}1285 1286void intel_uc_fw_fini(struct intel_uc_fw *uc_fw)1287{1288 uc_fw_unbind_ggtt(uc_fw);1289 uc_fw_rsa_data_destroy(uc_fw);1290 1291 if (i915_gem_object_has_pinned_pages(uc_fw->obj))1292 i915_gem_object_unpin_pages(uc_fw->obj);1293 1294 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_AVAILABLE);1295}1296 1297void intel_uc_fw_resume_mapping(struct intel_uc_fw *uc_fw)1298{1299 if (!intel_uc_fw_is_available(uc_fw))1300 return;1301 1302 if (!i915_gem_object_has_pinned_pages(uc_fw->obj))1303 return;1304 1305 uc_fw_bind_ggtt(uc_fw);1306}1307 1308/**1309 * intel_uc_fw_cleanup_fetch - cleanup uC firmware1310 * @uc_fw: uC firmware1311 *1312 * Cleans up uC firmware by releasing the firmware GEM obj.1313 */1314void intel_uc_fw_cleanup_fetch(struct intel_uc_fw *uc_fw)1315{1316 if (!intel_uc_fw_is_available(uc_fw))1317 return;1318 1319 i915_gem_object_put(fetch_and_zero(&uc_fw->obj));1320 1321 intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_SELECTED);1322}1323 1324/**1325 * intel_uc_fw_copy_rsa - copy fw RSA to buffer1326 *1327 * @uc_fw: uC firmware1328 * @dst: dst buffer1329 * @max_len: max number of bytes to copy1330 *1331 * Return: number of copied bytes.1332 */1333size_t intel_uc_fw_copy_rsa(struct intel_uc_fw *uc_fw, void *dst, u32 max_len)1334{1335 struct intel_memory_region *mr = uc_fw->obj->mm.region;1336 u32 size = min_t(u32, uc_fw->rsa_size, max_len);1337 u32 offset = uc_fw->dma_start_offset + sizeof(struct uc_css_header) + uc_fw->ucode_size;1338 struct sgt_iter iter;1339 size_t count = 0;1340 int idx;1341 1342 /* Called during reset handling, must be atomic [no fs_reclaim] */1343 GEM_BUG_ON(!intel_uc_fw_is_available(uc_fw));1344 1345 idx = offset >> PAGE_SHIFT;1346 offset = offset_in_page(offset);1347 if (i915_gem_object_has_struct_page(uc_fw->obj)) {1348 struct page *page;1349 1350 for_each_sgt_page(page, iter, uc_fw->obj->mm.pages) {1351 u32 len = min_t(u32, size, PAGE_SIZE - offset);1352 1353 if (idx > 0) {1354 idx--;1355 continue;1356 }1357 1358 memcpy_from_page(dst, page, offset, len);1359 1360 offset = 0;1361 dst += len;1362 size -= len;1363 count += len;1364 if (!size)1365 break;1366 }1367 } else {1368 dma_addr_t addr;1369 1370 for_each_sgt_daddr(addr, iter, uc_fw->obj->mm.pages) {1371 u32 len = min_t(u32, size, PAGE_SIZE - offset);1372 void __iomem *vaddr;1373 1374 if (idx > 0) {1375 idx--;1376 continue;1377 }1378 1379 vaddr = io_mapping_map_atomic_wc(&mr->iomap,1380 addr - mr->region.start);1381 memcpy_fromio(dst, vaddr + offset, len);1382 io_mapping_unmap_atomic(vaddr);1383 1384 offset = 0;1385 dst += len;1386 size -= len;1387 count += len;1388 if (!size)1389 break;1390 }1391 }1392 1393 return count;1394}1395 1396/**1397 * intel_uc_fw_dump - dump information about uC firmware1398 * @uc_fw: uC firmware1399 * @p: the &drm_printer1400 *1401 * Pretty printer for uC firmware.1402 */1403void intel_uc_fw_dump(const struct intel_uc_fw *uc_fw, struct drm_printer *p)1404{1405 bool got_wanted;1406 1407 drm_printf(p, "%s firmware: %s\n",1408 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path);1409 if (uc_fw->file_selected.path != uc_fw->file_wanted.path)1410 drm_printf(p, "%s firmware wanted: %s\n",1411 intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_wanted.path);1412 drm_printf(p, "\tstatus: %s\n",1413 intel_uc_fw_status_repr(uc_fw->status));1414 1415 if (uc_fw->file_selected.ver.major < uc_fw->file_wanted.ver.major)1416 got_wanted = false;1417 else if ((uc_fw->file_selected.ver.major == uc_fw->file_wanted.ver.major) &&1418 (uc_fw->file_selected.ver.minor < uc_fw->file_wanted.ver.minor))1419 got_wanted = false;1420 else if ((uc_fw->file_selected.ver.major == uc_fw->file_wanted.ver.major) &&1421 (uc_fw->file_selected.ver.minor == uc_fw->file_wanted.ver.minor) &&1422 (uc_fw->file_selected.ver.patch < uc_fw->file_wanted.ver.patch))1423 got_wanted = false;1424 else1425 got_wanted = true;1426 1427 if (!got_wanted)1428 drm_printf(p, "\tversion: wanted %u.%u.%u, found %u.%u.%u\n",1429 uc_fw->file_wanted.ver.major,1430 uc_fw->file_wanted.ver.minor,1431 uc_fw->file_wanted.ver.patch,1432 uc_fw->file_selected.ver.major,1433 uc_fw->file_selected.ver.minor,1434 uc_fw->file_selected.ver.patch);1435 else1436 drm_printf(p, "\tversion: found %u.%u.%u\n",1437 uc_fw->file_selected.ver.major,1438 uc_fw->file_selected.ver.minor,1439 uc_fw->file_selected.ver.patch);1440 drm_printf(p, "\tuCode: %u bytes\n", uc_fw->ucode_size);1441 drm_printf(p, "\tRSA: %u bytes\n", uc_fw->rsa_size);1442}1443