/* * List of required GuC and HuC binaries per-platform. They must be ordered * based on platform, from newer to older. * * Versioning follows the guidelines from * Documentation/driver-api/firmware/firmware-usage-guidelines.rst. There is a * distinction for platforms being officially supported by the driver or not. * Platforms not available publicly or not yet officially supported by the * driver (under force-probe), use the mmp_ver(): the firmware autoselect logic * will select the firmware from disk with filename that matches the full * "mpp version", i.e. major.minor.patch. mmp_ver() should only be used for * this case. * * For platforms officially supported by the driver, the filename always only * ever contains the major version (GuC) or no version at all (HuC). * * After loading the file, the driver parses the versions embedded in the blob. * The major version needs to match a major version supported by the driver (if * any). The minor version is also checked and a notice emitted to the log if * the version found is smaller than the version wanted. This is done only for * informational purposes so users may have a chance to upgrade, but the driver * still loads and use the older firmware. * * Examples: * * 1) Platform officially supported by i915 - using Tigerlake as example. * Driver loads the following firmware blobs from disk: * * - i915/tgl_guc_<major>.bin * - i915/tgl_huc.bin * * <major> number for GuC is checked that it matches the version inside * the blob. <minor> version is checked and if smaller than the expected * an info message is emitted about that. * * 1) XE_<FUTUREINTELPLATFORM>, still under require_force_probe. Using * "wipplat" as a short-name. Driver loads the following firmware blobs * from disk: * * - xe/wipplat_guc_<major>.<minor>.<patch>.bin * - xe/wipplat_huc_<major>.<minor>.<patch>.bin * * <major> and <minor> are checked that they match the version inside * the blob. Both of them need to match exactly what the driver is * expecting, otherwise it fails. * * 3) Platform officially supported by xe and out of force-probe. Using * "plat" as a short-name. Except for the different directory, the * behavior is the same as (1). Driver loads the following firmware * blobs from disk: * * - xe/plat_guc_<major>.bin * - xe/plat_huc.bin * * <major> number for GuC is checked that it matches the version inside * the blob. <minor> version is checked and if smaller than the expected * an info message is emitted about that. * * For the platforms already released with a major version, they should never be * removed from the table. Instead new entries with newer versions may be added * before them, so they take precedence. * * TODO: Currently there's no fallback on major version. That's because xe * driver only supports the one major version of each firmware in the table. * This needs to be fixed when the major version of GuC is updated.
*/
/* for the GSC FW we match the compatibility version and not the release one */ #define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver) \
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, gsc, lnl, 104, 1, 0)) \
fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, gsc, mtl, 102, 1, 0))
#define fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c) \
MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a ## . ## b ## . ## c)) #define fw_filename_major_ver(dir_, uc_, shortname_, a, b, c) \
MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a)) #define fw_filename_no_ver(dir_, uc_, shortname_) \
MAKE_FW_PATH(dir_, uc_, shortname_, "") #define fw_filename_gsc(dir_, uc_, shortname_, a, b, c) \
MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(b))
#define uc_fw_entry_mmp_ver(dir_, uc_, shortname_, a, b, c) \
{ fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c), \
a, b, c, true } #define uc_fw_entry_major_ver(dir_, uc_, shortname_, a, b, c) \
{ fw_filename_major_ver(dir_, uc_, shortname_, a, b, c), \
a, b, c } #define uc_fw_entry_no_ver(dir_, uc_, shortname_) \
{ fw_filename_no_ver(dir_, uc_, shortname_), \
0, 0 } #define uc_fw_entry_gsc(dir_, uc_, shortname_, a, b, c) \
{ fw_filename_gsc(dir_, uc_, shortname_, a, b, c), \
a, b, c }
/* All blobs need to be declared via MODULE_FIRMWARE() */ #define XE_UC_MODULE_FIRMWARE(platform__, gt_type__, fw_filename) \
MODULE_FIRMWARE(fw_filename);
/* Driver has no requirement on any version, any is good. */ if (!wanted->major) return 0;
/* * If full version is required, both major and minor should match. * Otherwise, at least the major version.
*/ if (wanted->major != found->major ||
(uc_fw->full_ver_required &&
((wanted->minor != found->minor) ||
(wanted->patch != found->patch)))) {
drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u.%u != %u.%u.%u\n",
xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
found->major, found->minor, found->patch,
wanted->major, wanted->minor, wanted->patch); goto fail;
}
if (wanted->minor > found->minor ||
(wanted->minor == found->minor && wanted->patch > found->patch)) {
drm_notice(&xe->drm, "%s firmware (%u.%u.%u) is recommended, but only (%u.%u.%u) was found in %s\n",
xe_uc_fw_type_repr(uc_fw->type),
wanted->major, wanted->minor, wanted->patch,
found->major, found->minor, found->patch,
uc_fw->path);
drm_info(&xe->drm, "Consider updating your linux-firmware pkg or downloading from %s\n",
XE_UC_FIRMWARE_URL);
}
return 0;
fail: if (xe_uc_fw_is_overridden(uc_fw)) return 0;
/* Look for the manifest first */
offset = entry_offset(header, manifest_entry); if (!offset) {
xe_gt_err(gt, "Failed to find %s manifest!\n",
xe_uc_fw_type_repr(uc_fw->type)); return -ENODATA;
}
/* then optionally look for the css header */ if (css_entry) { int ret;
/* * This section does not contain a CSS entry on DG2. We * don't support DG2 HuC right now, so no need to handle * it, just add a reminder in case that changes.
*/
xe_assert(xe, xe->info.platform != XE_DG2);
offset = entry_offset(header, css_entry);
/* the CSS header parser will check that the CSS header fits */ if (offset > size) {
xe_gt_err(gt, "FW too small! %zu < %u\n", size, offset); return -ENODATA;
}
ret = parse_css_header(uc_fw, data + offset, size - offset); if (ret) return ret;
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;
}
/* Only GuC/HuC are supported */ if (uc_fw->type != XE_UC_FW_TYPE_GUC && uc_fw->type != XE_UC_FW_TYPE_HUC)
uc_fw->path = NULL;
/* VF will support only firmwares that driver can autoselect */
xe_uc_fw_change_status(uc_fw, uc_fw->path ?
XE_UC_FIRMWARE_PRELOADED :
XE_UC_FIRMWARE_NOT_SUPPORTED);
if (!xe_uc_fw_is_supported(uc_fw)) return;
/* PF is doing the loading, so we don't need a path on the VF */
uc_fw->path = "Loaded by PF";
/* The GuC versions are set up during the VF bootstrap */ if (uc_fw->type == XE_UC_FW_TYPE_GUC) {
uc_fw->versions.wanted_type = XE_UC_FW_VER_COMPATIBILITY;
xe_gt_sriov_vf_guc_versions(uc_fw_to_gt(uc_fw), wanted, compat);
}
}
/* * 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(XE_UC_FIRMWARE_UNINITIALIZED);
xe_gt_assert(gt, !uc_fw->status);
xe_gt_assert(gt, !uc_fw->path);
uc_fw_auto_select(xe, uc_fw);
if (IS_SRIOV_VF(xe)) {
uc_fw_vf_override(uc_fw); return 0;
}
if (!xe_uc_fw_is_supported(uc_fw)) { if (uc_fw->type == XE_UC_FW_TYPE_GUC) {
xe_gt_err(gt, "No GuC firmware defined for platform\n"); return -ENOENT;
} return 0;
}
/* an empty path means the firmware is disabled */ if (!xe_device_uc_enabled(xe) || !(*uc_fw->path)) {
xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_DISABLED);
xe_gt_dbg(gt, "%s disabled\n", xe_uc_fw_type_repr(uc_fw->type)); return 0;
}
err = request_firmware(&fw, uc_fw->path, dev); if (err) goto fail;
err = parse_headers(uc_fw, fw); if (err) goto fail;
print_uc_fw_version(&p,
&uc_fw->versions.found[XE_UC_FW_VER_RELEASE], "Using %s firmware from %s",
xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
/* for GSC FW we want the compatibility version, which we query after load */ if (uc_fw->type != XE_UC_FW_TYPE_GSC) {
err = xe_uc_fw_check_version_requirements(uc_fw); if (err) goto fail;
}
/* Set the source address for the uCode */
src_offset = uc_fw_ggtt_offset(uc_fw) + uc_fw->css_offset;
xe_mmio_write32(mmio, DMA_ADDR_0_LOW, lower_32_bits(src_offset));
xe_mmio_write32(mmio, DMA_ADDR_0_HIGH,
upper_32_bits(src_offset) | DMA_ADDRESS_SPACE_GGTT);
/* Set the DMA destination */
xe_mmio_write32(mmio, DMA_ADDR_1_LOW, offset);
xe_mmio_write32(mmio, 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
*/
xe_mmio_write32(mmio, DMA_COPY_SIZE, sizeof(struct uc_css_header) + uc_fw->ucode_size);
/* Start the DMA */
xe_mmio_write32(mmio, DMA_CTRL,
_MASKED_BIT_ENABLE(dma_flags | START_DMA));
/* Wait for DMA to finish */
ret = xe_mmio_wait32(mmio, DMA_CTRL, START_DMA, 0, 100000, &dma_ctrl, false); if (ret)
drm_err(&xe->drm, "DMA for %s fw failed, DMA_CTRL=%u\n",
xe_uc_fw_type_repr(uc_fw->type), dma_ctrl);
/* Disable the bits once DMA is over */
xe_mmio_write32(mmio, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags));
return ret;
}
int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags)
{ struct xe_device *xe = uc_fw_to_xe(uc_fw); int err;
/* make sure the status was cleared the last time we reset the uc */
xe_assert(xe, !xe_uc_fw_is_loaded(uc_fw));
if (!xe_uc_fw_is_loadable(uc_fw)) return -ENOEXEC;
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.