/* * List of required GuC and HuC binaries per-platform. * Must be ordered based on platform + revid, from newer to older. * * Note that RKL and ADL-S have the same GuC/HuC device ID's and use the same * firmware as TGL. * * Version numbers: * Originally, the driver required an exact match major/minor/patch furmware * file and only supported that one version for any given platform. However, * the new direction from upstream is to be backwards compatible with all * prior releases and to be as flexible as possible as to what firmware is * loaded. * * For GuC, the major version number signifies a backwards breaking API change. * So, new format GuC firmware files are labelled by their major version only. * For HuC, there is no KMD interaction, hence no version matching requirement. * So, new format HuC firmware files have no version number at all. * * All of which means that the table below must keep all old format files with * full three point version number. But newer files have reduced requirements. * Having said that, the driver still needs to track the minor version number * for GuC at least. As it is useful to report to the user that they are not * running with a recent enough version for all KMD supported features, * security fixes, etc. to be enabled.
*/ #define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \
fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 12, 1)) \
fw_def(DG2, 0, guc_maj(dg2, 70, 12, 1)) \
fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 12, 1)) \
fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \
fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \
fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 12, 1)) \
fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \
fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \
fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \
fw_def(ROCKETLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \
fw_def(TIGERLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \
fw_def(JASPERLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \
fw_def(ELKHARTLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \
fw_def(ICELAKE, 0, guc_mmp(icl, 70, 1, 1)) \
fw_def(COMETLAKE, 5, guc_mmp(cml, 70, 1, 1)) \
fw_def(COMETLAKE, 0, guc_mmp(kbl, 70, 1, 1)) \
fw_def(COFFEELAKE, 0, guc_mmp(kbl, 70, 1, 1)) \
fw_def(GEMINILAKE, 0, guc_mmp(glk, 70, 1, 1)) \
fw_def(KABYLAKE, 0, guc_mmp(kbl, 70, 1, 1)) \
fw_def(BROXTON, 0, guc_mmp(bxt, 70, 1, 1)) \
fw_def(SKYLAKE, 0, guc_mmp(skl, 70, 1, 1))
/* * The GSC FW has multiple version (see intel_gsc_uc.h for details); since what * we care about is the interface, we use the compatibility version in the * binary names. * Same as with the GuC, a major version bump indicate a * backward-incompatible change, while a minor version bump indicates a * backward-compatible one, so we use only the former in the file name.
*/ #define INTEL_GSC_FIRMWARE_DEFS(fw_def, gsc_def) \
fw_def(METEORLAKE, 0, gsc_def(mtl, 1, 0))
/* * Set of macros for producing a list of filenames from the above table.
*/ #define __MAKE_UC_FW_PATH_BLANK(prefix_, name_) \ "i915/" \
__stringify(prefix_) "_" name_ ".bin"
/* Minor for internal driver use, not part of file name */ #define MAKE_GUC_FW_PATH_MAJOR(prefix_, major_, minor_, patch_) \
__MAKE_UC_FW_PATH_MAJOR(prefix_, "guc", major_)
/* * All blobs need to be declared via MODULE_FIRMWARE(). * This first expansion of the table macros is solely to provide * that declaration.
*/ #define INTEL_UC_MODULE_FW(platform_, revid_, uc_) \
MODULE_FIRMWARE(uc_);
/* * The next expansion of the table macros (in __uc_fw_auto_select below) provides * actual data structures with both the filename and the version information. * These structure arrays are then iterated over to the list of suitable files * for the current platform and to then attempt to load those files, in the order * listed, until one is successfully found.
*/ struct __packed uc_fw_blob { constchar *path; bool legacy;
u8 major;
u8 minor;
u8 patch; bool has_gsc_headers;
};
/* * The only difference between the ADL GuC FWs is the HWConfig support. * ADL-N does not support HWConfig, so we should use the same binary as * ADL-S, otherwise the GuC might attempt to fetch a config table that * does not exist.
*/ if (IS_ALDERLAKE_P_N(i915))
p = INTEL_ALDERLAKE_S;
found = false; for (i = 0; i < fw_count && p <= fw_blobs[i].p; i++) { conststruct uc_fw_blob *blob = &fw_blobs[i].blob;
if (p != fw_blobs[i].p) continue;
if (rev < fw_blobs[i].rev) continue;
if (uc_fw->file_selected.path) { /* * Continuing an earlier search after a found blob failed to load. * Once the previously chosen path has been found, clear it out * and let the search continue from there.
*/ if (uc_fw->file_selected.path == blob->path)
uc_fw->file_selected.path = NULL;
/* make sure the list is ordered as expected */ for (i = 1; i < fw_count; i++) { /* Versionless file names must be unique per platform: */ for (j = i + 1; j < fw_count; j++) { /* Same platform? */ if (fw_blobs[i].p != fw_blobs[j].p) continue;
if (fw_blobs[i].blob.path != fw_blobs[j].blob.path) continue;
/* Next platform is good: */ if (fw_blobs[i].p < fw_blobs[i - 1].p) continue;
/* Next platform revision is good: */ if (fw_blobs[i].p == fw_blobs[i - 1].p &&
fw_blobs[i].rev < fw_blobs[i - 1].rev) continue;
/* Platform/revision must be in order: */ if (fw_blobs[i].p != fw_blobs[i - 1].p ||
fw_blobs[i].rev != fw_blobs[i - 1].rev) goto bad;
/* Next major version is good: */ if (fw_blobs[i].blob.major < fw_blobs[i - 1].blob.major) continue;
/* New must be before legacy: */ if (!fw_blobs[i].blob.legacy && fw_blobs[i - 1].blob.legacy) goto bad;
/* New to legacy also means 0.0 to X.Y (HuC), or X.0 to X.Y (GuC) */ if (fw_blobs[i].blob.legacy && !fw_blobs[i - 1].blob.legacy) { if (!fw_blobs[i - 1].blob.major) continue;
if (fw_blobs[i].blob.major == fw_blobs[i - 1].blob.major) continue;
}
/* Major versions must be in order: */ if (fw_blobs[i].blob.major != fw_blobs[i - 1].blob.major) goto bad;
/* Next minor version is good: */ if (fw_blobs[i].blob.minor < fw_blobs[i - 1].blob.minor) continue;
/* Minor versions must be in order: */ if (fw_blobs[i].blob.minor != fw_blobs[i - 1].blob.minor) goto bad;
/* Patch versions must be in order and unique: */ if (fw_blobs[i].blob.patch < fw_blobs[i - 1].blob.patch) continue;
/** * intel_uc_fw_init_early - initialize the uC object and select the firmware * @uc_fw: uC firmware * @type: type of uC * @needs_ggtt_mapping: whether the FW needs to be GGTT mapped for loading * * Initialize the state of our uC object and relevant tracking and select the * firmware to fetch and load.
*/ void intel_uc_fw_init_early(struct intel_uc_fw *uc_fw, enum intel_uc_fw_type type, bool needs_ggtt_mapping)
{ struct intel_gt *gt = ____uc_fw_to_gt(uc_fw, type); struct drm_i915_private *i915 = gt->i915;
/* * we use FIRMWARE_UNINITIALIZED to detect checks against uc_fw->status * before we're looked at the HW caps to see if we have uc support
*/
BUILD_BUG_ON(INTEL_UC_FIRMWARE_UNINITIALIZED);
GEM_BUG_ON(uc_fw->status);
GEM_BUG_ON(uc_fw->file_selected.path);
/* * The GuC firmware includes an extra version number to specify the * submission API level. This allows submission code to work with * multiple GuC versions without having to know the absolute firmware * version number (there are likely to be multiple firmware releases * which all support the same submission API level). * * Note that the spec for the CSS header defines this version number * as 'vf_version' as it was originally intended for virtualisation. * However, it is applicable to native submission as well. * * Unfortunately, due to an oversight, this version number was only * exposed in the CSS header from v70.6.0.
*/ if (uc_fw->file_selected.ver.major >= 70) { if (uc_fw->file_selected.ver.minor >= 6) { /* v70.6.0 adds CSS header support */
uc_unpack_css_version(&guc->submission_version, css->vf_version);
} elseif (uc_fw->file_selected.ver.minor >= 3) { /* v70.3.0 introduced v1.1.0 */
guc->submission_version.major = 1;
guc->submission_version.minor = 1;
guc->submission_version.patch = 0;
} else { /* v70.0.0 introduced v1.0.0 */
guc->submission_version.major = 1;
guc->submission_version.minor = 0;
guc->submission_version.patch = 0;
}
} elseif (uc_fw->file_selected.ver.major >= 69) { /* v69.0.0 introduced v0.10.0 */
guc->submission_version.major = 0;
guc->submission_version.minor = 10;
guc->submission_version.patch = 0;
} else { /* Prior versions were v0.1.0 */
guc->submission_version.major = 0;
guc->submission_version.minor = 1;
guc->submission_version.patch = 0;
}
/* * GuC version number components are defined as being 8-bits. * The submission code relies on this to optimise version comparison * tests. So enforce the restriction here.
*/
/* * MTL has some compatibility issues with early GuC/HuC binaries * not working with newer ones. This is specific to MTL and we * don't expect it to extend to other platforms.
*/ if (IS_METEORLAKE(gt->i915) && uc_fw->type == INTEL_UC_FW_TYPE_HUC) {
ret = check_mtl_huc_guc_compatibility(gt, selected); if (ret) return ret;
}
if (!wanted->ver.major || !selected->ver.major) return 0;
/* Check the file's major version was as it claimed */ if (selected->ver.major != wanted->ver.major) {
UNEXPECTED(gt, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",
intel_uc_fw_type_repr(uc_fw->type), selected->path,
selected->ver.major, selected->ver.minor,
wanted->ver.major, wanted->ver.minor); if (!intel_uc_fw_is_overridden(uc_fw)) return -ENOEXEC;
} elseif (old_ver) { if (selected->ver.minor < wanted->ver.minor)
*old_ver = true; elseif ((selected->ver.minor == wanted->ver.minor) &&
(selected->ver.patch < wanted->ver.patch))
*old_ver = true;
}
/* Any error is terminal if overriding. Don't bother searching for older versions */ if (err && intel_uc_fw_is_overridden(uc_fw)) goto fail;
while (err == -ENOENT) {
old_ver = true;
__uc_fw_auto_select(i915, uc_fw); if (!uc_fw->file_selected.path) { /* * No more options! But set the path back to something * valid just in case it gets dereferenced.
*/
uc_fw->file_selected.path = file_ideal.path;
/* Also, preserve the version that was really wanted */
memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted)); break;
}
err = try_firmware_load(uc_fw, &fw);
}
if (err) goto fail;
err = check_fw_header(gt, fw, uc_fw); if (err) goto fail;
if (uc_fw->type == INTEL_UC_FW_TYPE_GUC) {
err = guc_check_version_range(uc_fw); if (err) goto fail;
}
err = intel_uc_check_file_version(uc_fw, &old_ver); if (err) goto fail;
if (old_ver && uc_fw->file_selected.ver.major) { /* Preserve the version that was really wanted */
memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted));
UNEXPECTED(gt, "%s firmware %s (%d.%d.%d) is recommended, but only %s (%d.%d.%d) was found\n",
intel_uc_fw_type_repr(uc_fw->type),
uc_fw->file_wanted.path,
uc_fw->file_wanted.ver.major,
uc_fw->file_wanted.ver.minor,
uc_fw->file_wanted.ver.patch,
uc_fw->file_selected.path,
uc_fw->file_selected.ver.major,
uc_fw->file_selected.ver.minor,
uc_fw->file_selected.ver.patch);
gt_info(gt, "Consider updating your linux-firmware pkg or downloading from %s\n",
INTEL_UC_FIRMWARE_URL);
}
/* * The media GT shares the GGTT with the root GT, which means that * we need to use different offsets for the binaries on the media GT. * To keep the math simple, we use 8MB for the root tile and 8MB for * the media one. This will need to be updated if we ever have more * than 1 media GT.
*/
BUILD_BUG_ON(INTEL_UC_FW_NUM_TYPES * INTEL_UC_RSVD_GGTT_PER_FW > SZ_8M);
GEM_BUG_ON(gt->type == GT_MEDIA && gt->info.id > 1); if (gt->type == GT_MEDIA)
offset += SZ_8M;
/* Set the source address for the uCode */
offset = uc_fw->vma_res.start + uc_fw->dma_start_offset;
GEM_BUG_ON(upper_32_bits(offset) & 0xFFFF0000);
intel_uncore_write_fw(uncore, DMA_ADDR_0_LOW, lower_32_bits(offset));
intel_uncore_write_fw(uncore, DMA_ADDR_0_HIGH, upper_32_bits(offset));
/* Set the DMA destination */
intel_uncore_write_fw(uncore, DMA_ADDR_1_LOW, dst_offset);
intel_uncore_write_fw(uncore, DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);
/* * Set the transfer size. The header plus uCode will be copied to WOPCM * via DMA, excluding any other components
*/
intel_uncore_write_fw(uncore, DMA_COPY_SIZE, sizeof(struct uc_css_header) + uc_fw->ucode_size);
/* Start the DMA */
intel_uncore_write_fw(uncore, DMA_CTRL,
_MASKED_BIT_ENABLE(dma_flags | START_DMA));
/* Wait for DMA to finish */
ret = intel_wait_for_register_fw(uncore, DMA_CTRL, START_DMA, 0, 100, NULL); if (ret)
gt_err(gt, "DMA for %s fw failed, DMA_CTRL=%u\n",
intel_uc_fw_type_repr(uc_fw->type),
intel_uncore_read_fw(uncore, DMA_CTRL));
/* Disable the bits once DMA is over */
intel_uncore_write_fw(uncore, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags));
staticinlinebool uc_fw_need_rsa_in_memory(struct intel_uc_fw *uc_fw)
{ /* * The HW reads the GuC RSA from memory if the key size is > 256 bytes, * while it reads it from the 64 RSA registers if it is smaller. * The HuC RSA is always read from memory.
*/ return uc_fw->type == INTEL_UC_FW_TYPE_HUC || uc_fw->rsa_size > 256;
}
err = i915_inject_probe_error(gt->i915, -ENXIO); if (err) return err;
if (!uc_fw_need_rsa_in_memory(uc_fw)) return 0;
/* * uC firmwares will sit above GUC_GGTT_TOP and will not map through * GGTT. Unfortunately, this means that the GuC HW cannot perform the uC * authentication from memory, as the RSA offset now falls within the * GuC inaccessible range. We resort to perma-pinning an additional vma * within the accessible range that only contains the RSA signature. * The GuC HW can use this extra pinning to perform the authentication * since its GGTT offset will be GuC accessible.
*/
GEM_BUG_ON(uc_fw->rsa_size > PAGE_SIZE);
vma = intel_guc_allocate_vma(gt_to_guc(gt), PAGE_SIZE); if (IS_ERR(vma)) return PTR_ERR(vma);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.