drm/xe: Update GuC/HuC firmware autoselect logic
Update the logic to autoselect GuC/HuC for the platforms with the following improvements: - Document what is the firmware file that is expected to be loaded and what is checked from blob headers - When the platform is under force-probe it's desired to enforce the full-version requirement so the correct firmware is used before widespread adoption and backward-compatibility commitments - Directory from which we expect firmware blobs to be available in upstream linux-firmware repository depends on the platform: for the ones supported by i915 it uses the i915/ directory, but the ones expected to be supported by xe, it's on the xe/ directory. This means that for platforms in the intersection, the firmware is loaded from a different directory, but that is not much important in the firmware repo and it avoids firmware duplication. - Make the table with the firmware definitions clearly state the versions being expected. Now with macros to select the version it's possible to choose between full-version/major-version for GuC and full-version/no-version for HuC. These are similar to the macros used in i915, but implemented in a slightly different way to avoid duplicating the macros for each firmware/type and functionality, besides adding the support for different directories. - There is no check added regarding force-probe since xe should reuse the same firmware files published for i915 for past platforms. This can be improved later with additional kunit checking against a hardcoded list of platforms that falls in this category. - As mentioned in the TODO, the major version fallback was not implemented before as currently each platform only supports one major. That can be easily added later. - GuC version for MTL and PVC were updated to 70.6.4, using the exact full version, while the After this the GuC firmware used by PVC changes to pvc_guc_70.5.2.bin since it's using a file not published yet. Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com> Reviewed-by: Anusha Srivatsa <anusha.srivatsa@intel.com> Link: https://lore.kernel.org/r/20230324051754.1346390-4-lucas.demarchi@intel.com Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
This commit is contained in:
parent
b9d773fc51
commit
ad55ead7f3
@ -17,6 +17,137 @@
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_uc_fw.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct uc_fw_entry {
|
||||
enum xe_platform platform;
|
||||
struct {
|
||||
const char *path;
|
||||
u16 major;
|
||||
u16 minor;
|
||||
bool full_ver_required;
|
||||
};
|
||||
};
|
||||
|
||||
struct fw_blobs_by_type {
|
||||
const struct uc_fw_entry *entries;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
#define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \
|
||||
fw_def(METEORLAKE, mmp_ver( i915, guc, mtl, 70, 6, 4)) \
|
||||
fw_def(PVC, mmp_ver( xe, guc, pvc, 70, 6, 4)) \
|
||||
fw_def(DG2, major_ver(i915, guc, dg2, 70, 5)) \
|
||||
fw_def(DG1, major_ver(i915, guc, dg1, 70, 5)) \
|
||||
fw_def(ALDERLAKE_P, major_ver(i915, guc, adlp, 70, 5)) \
|
||||
fw_def(ALDERLAKE_S, major_ver(i915, guc, tgl, 70, 5)) \
|
||||
fw_def(TIGERLAKE, major_ver(i915, guc, tgl, 70, 5))
|
||||
|
||||
#define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver) \
|
||||
fw_def(ALDERLAKE_S, no_ver(i915, huc, tgl)) \
|
||||
fw_def(DG1, no_ver(i915, huc, dg1)) \
|
||||
fw_def(TIGERLAKE, no_ver(i915, huc, tgl))
|
||||
|
||||
#define MAKE_FW_PATH(dir__, uc__, shortname__, version__) \
|
||||
__stringify(dir__) "/" __stringify(shortname__) "_" __stringify(uc__) version__ ".bin"
|
||||
|
||||
#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) \
|
||||
MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a))
|
||||
#define fw_filename_no_ver(dir_, uc_, shortname_) \
|
||||
MAKE_FW_PATH(dir_, uc_, shortname_, "")
|
||||
|
||||
#define uc_fw_entry_mmp_ver(dir_, uc_, shortname_, a, b, c) \
|
||||
{ fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c), \
|
||||
a, b, true }
|
||||
#define uc_fw_entry_major_ver(dir_, uc_, shortname_, a, b) \
|
||||
{ fw_filename_major_ver(dir_, uc_, shortname_, a, b), \
|
||||
a, b }
|
||||
#define uc_fw_entry_no_ver(dir_, uc_, shortname_) \
|
||||
{ fw_filename_no_ver(dir_, uc_, shortname_), \
|
||||
0, 0 }
|
||||
|
||||
/* All blobs need to be declared via MODULE_FIRMWARE() */
|
||||
#define XE_UC_MODULE_FIRMWARE(platform__, fw_filename) \
|
||||
MODULE_FIRMWARE(fw_filename);
|
||||
|
||||
#define XE_UC_FW_ENTRY(platform__, entry__) \
|
||||
{ \
|
||||
.platform = XE_ ## platform__, \
|
||||
entry__, \
|
||||
},
|
||||
|
||||
XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, \
|
||||
fw_filename_mmp_ver, fw_filename_major_ver)
|
||||
XE_HUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, \
|
||||
fw_filename_mmp_ver, fw_filename_no_ver)
|
||||
|
||||
static struct xe_gt *
|
||||
__uc_fw_to_gt(struct xe_uc_fw *uc_fw, enum xe_uc_fw_type type)
|
||||
{
|
||||
@ -37,123 +168,38 @@ static struct xe_device *uc_fw_to_xe(struct xe_uc_fw *uc_fw)
|
||||
return gt_to_xe(uc_fw_to_gt(uc_fw));
|
||||
}
|
||||
|
||||
/*
|
||||
* List of required GuC and HuC binaries per-platform.
|
||||
* Must be ordered based on platform, from newer to older.
|
||||
*/
|
||||
#define XE_GUC_FIRMWARE_DEFS(fw_def, guc_def) \
|
||||
fw_def(METEORLAKE, guc_def(mtl, 70, 5, 2)) \
|
||||
fw_def(PVC, guc_def(pvc, 70, 5, 2)) \
|
||||
fw_def(DG2, guc_def(dg2, 70, 5, 2)) \
|
||||
fw_def(DG1, guc_def(dg1, 70, 5, 2)) \
|
||||
fw_def(ALDERLAKE_P, guc_def(adlp, 70, 5, 2)) \
|
||||
fw_def(ALDERLAKE_S, guc_def(tgl, 70, 5, 2)) \
|
||||
fw_def(TIGERLAKE, guc_def(tgl, 70, 5, 2))
|
||||
|
||||
#define XE_HUC_FIRMWARE_DEFS(fw_def, huc_def, huc_ver) \
|
||||
fw_def(ALDERLAKE_S, huc_def(tgl)) \
|
||||
fw_def(DG1, huc_def(dg1)) \
|
||||
fw_def(TIGERLAKE, huc_def(tgl))
|
||||
|
||||
#define __MAKE_HUC_FW_PATH(prefix_, name_) \
|
||||
"i915/" \
|
||||
__stringify(prefix_) "_" name_ ".bin"
|
||||
|
||||
#define __MAKE_UC_FW_PATH_MAJOR(prefix_, name_, major_) \
|
||||
"i915/" \
|
||||
__stringify(prefix_) "_" name_ "_" \
|
||||
__stringify(major_) ".bin"
|
||||
|
||||
#define __MAKE_UC_FW_PATH_FULL_VER(prefix_, name_, major_, minor_, patch_) \
|
||||
"i915/" \
|
||||
__stringify(prefix_) "_" name_ "_" \
|
||||
__stringify(major_) "." \
|
||||
__stringify(minor_) "." \
|
||||
__stringify(patch_) ".bin"
|
||||
|
||||
#define MAKE_GUC_FW_PATH(prefix_, major_, minor_, patch_) \
|
||||
__MAKE_UC_FW_PATH_MAJOR(prefix_, "guc", major_)
|
||||
|
||||
#define MAKE_HUC_FW_PATH(prefix_) \
|
||||
__MAKE_HUC_FW_PATH(prefix_, "huc")
|
||||
|
||||
#define MAKE_HUC_FW_PATH_FULL_VER(prefix_, major_, minor_, patch_) \
|
||||
__MAKE_UC_FW_PATH_FULL_VER(prefix_, "huc", major_, minor_, patch_)
|
||||
|
||||
|
||||
/* All blobs need to be declared via MODULE_FIRMWARE() */
|
||||
#define XE_UC_MODULE_FW(platform_, uc_) \
|
||||
MODULE_FIRMWARE(uc_);
|
||||
|
||||
XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FW, MAKE_GUC_FW_PATH)
|
||||
XE_HUC_FIRMWARE_DEFS(XE_UC_MODULE_FW, MAKE_HUC_FW_PATH, MAKE_HUC_FW_PATH_FULL_VER)
|
||||
|
||||
/* The below structs and macros are used to iterate across the list of blobs */
|
||||
struct __packed uc_fw_blob {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
const char *path;
|
||||
};
|
||||
|
||||
#define UC_FW_BLOB(major_, minor_, path_) \
|
||||
{ .major = major_, .minor = minor_, .path = path_ }
|
||||
|
||||
#define GUC_FW_BLOB(prefix_, major_, minor_, patch_) \
|
||||
UC_FW_BLOB(major_, minor_, \
|
||||
MAKE_GUC_FW_PATH(prefix_, major_, minor_, patch_))
|
||||
|
||||
#define HUC_FW_BLOB(prefix_) \
|
||||
UC_FW_BLOB(0, 0, MAKE_HUC_FW_PATH(prefix_))
|
||||
|
||||
#define HUC_FW_VERSION_BLOB(prefix_, major_, minor_, bld_num_) \
|
||||
UC_FW_BLOB(major_, minor_, \
|
||||
MAKE_HUC_FW_PATH_FULL_VER(prefix_, major_, minor_, bld_num_))
|
||||
|
||||
struct uc_fw_platform_requirement {
|
||||
enum xe_platform p;
|
||||
const struct uc_fw_blob blob;
|
||||
};
|
||||
|
||||
#define MAKE_FW_LIST(platform_, uc_) \
|
||||
{ \
|
||||
.p = XE_##platform_, \
|
||||
.blob = uc_, \
|
||||
},
|
||||
|
||||
struct fw_blobs_by_type {
|
||||
const struct uc_fw_platform_requirement *blobs;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
static void
|
||||
uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw)
|
||||
{
|
||||
static const struct uc_fw_platform_requirement blobs_guc[] = {
|
||||
XE_GUC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB)
|
||||
static const struct uc_fw_entry entries_guc[] = {
|
||||
XE_GUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
|
||||
uc_fw_entry_mmp_ver,
|
||||
uc_fw_entry_major_ver)
|
||||
};
|
||||
static const struct uc_fw_platform_requirement blobs_huc[] = {
|
||||
XE_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB, HUC_FW_VERSION_BLOB)
|
||||
static const struct uc_fw_entry entries_huc[] = {
|
||||
XE_HUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
|
||||
uc_fw_entry_mmp_ver,
|
||||
uc_fw_entry_no_ver)
|
||||
};
|
||||
static const struct fw_blobs_by_type blobs_all[XE_UC_FW_NUM_TYPES] = {
|
||||
[XE_UC_FW_TYPE_GUC] = { blobs_guc, ARRAY_SIZE(blobs_guc) },
|
||||
[XE_UC_FW_TYPE_HUC] = { blobs_huc, ARRAY_SIZE(blobs_huc) },
|
||||
[XE_UC_FW_TYPE_GUC] = { entries_guc, ARRAY_SIZE(entries_guc) },
|
||||
[XE_UC_FW_TYPE_HUC] = { entries_huc, ARRAY_SIZE(entries_huc) },
|
||||
};
|
||||
static const struct uc_fw_platform_requirement *fw_blobs;
|
||||
static const struct uc_fw_entry *entries;
|
||||
enum xe_platform p = xe->info.platform;
|
||||
u32 fw_count;
|
||||
u32 count;
|
||||
int i;
|
||||
|
||||
XE_BUG_ON(uc_fw->type >= ARRAY_SIZE(blobs_all));
|
||||
fw_blobs = blobs_all[uc_fw->type].blobs;
|
||||
fw_count = blobs_all[uc_fw->type].count;
|
||||
entries = blobs_all[uc_fw->type].entries;
|
||||
count = blobs_all[uc_fw->type].count;
|
||||
|
||||
for (i = 0; i < fw_count && p <= fw_blobs[i].p; i++) {
|
||||
if (p == fw_blobs[i].p) {
|
||||
const struct uc_fw_blob *blob = &fw_blobs[i].blob;
|
||||
|
||||
uc_fw->path = blob->path;
|
||||
uc_fw->major_ver_wanted = blob->major;
|
||||
uc_fw->minor_ver_wanted = blob->minor;
|
||||
for (i = 0; i < count && p <= entries[i].platform; i++) {
|
||||
if (p == entries[i].platform) {
|
||||
uc_fw->path = entries[i].path;
|
||||
uc_fw->major_ver_wanted = entries[i].major;
|
||||
uc_fw->minor_ver_wanted = entries[i].minor;
|
||||
uc_fw->full_ver_required = entries[i].full_ver_required;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -227,6 +273,47 @@ static void guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css)
|
||||
uc_fw->private_data_size = css->private_data_size;
|
||||
}
|
||||
|
||||
static int uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw)
|
||||
{
|
||||
struct xe_device *xe = uc_fw_to_xe(uc_fw);
|
||||
|
||||
/* Driver has no requirement on any version, any is good. */
|
||||
if (!uc_fw->major_ver_wanted)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If full version is required, both major and minor should match.
|
||||
* Otherwise, at least the major version.
|
||||
*/
|
||||
if (uc_fw->major_ver_wanted != uc_fw->major_ver_found ||
|
||||
(uc_fw->full_ver_required &&
|
||||
uc_fw->minor_ver_wanted != uc_fw->minor_ver_found)) {
|
||||
drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",
|
||||
xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
|
||||
uc_fw->major_ver_found, uc_fw->minor_ver_found,
|
||||
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (uc_fw->minor_ver_wanted > uc_fw->minor_ver_found) {
|
||||
drm_notice(&xe->drm, "%s firmware (%u.%u) is recommended, but only (%u.%u) was found in %s\n",
|
||||
xe_uc_fw_type_repr(uc_fw->type),
|
||||
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted,
|
||||
uc_fw->major_ver_found, uc_fw->minor_ver_found,
|
||||
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;
|
||||
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
int xe_uc_fw_init(struct xe_uc_fw *uc_fw)
|
||||
{
|
||||
struct xe_device *xe = uc_fw_to_xe(uc_fw);
|
||||
@ -308,19 +395,9 @@ int xe_uc_fw_init(struct xe_uc_fw *uc_fw)
|
||||
uc_fw->minor_ver_found = FIELD_GET(CSS_SW_VERSION_UC_MINOR,
|
||||
css->sw_version);
|
||||
|
||||
if (uc_fw->major_ver_wanted) {
|
||||
if (uc_fw->major_ver_found != uc_fw->major_ver_wanted ||
|
||||
uc_fw->minor_ver_found < uc_fw->minor_ver_wanted) {
|
||||
drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",
|
||||
xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
|
||||
uc_fw->major_ver_found, uc_fw->minor_ver_found,
|
||||
uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted);
|
||||
if (!xe_uc_fw_is_overridden(uc_fw)) {
|
||||
err = -ENOEXEC;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
err = uc_fw_check_version_requirements(uc_fw);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
if (uc_fw->type == XE_UC_FW_TYPE_GUC)
|
||||
guc_read_css_info(uc_fw, css);
|
||||
|
@ -175,6 +175,6 @@ static inline u32 xe_uc_fw_get_upload_size(struct xe_uc_fw *uc_fw)
|
||||
return __xe_uc_fw_get_upload_size(uc_fw);
|
||||
}
|
||||
|
||||
#define XE_UC_FIRMWARE_URL "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/i915"
|
||||
#define XE_UC_FIRMWARE_URL "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
|
||||
|
||||
#endif
|
||||
|
@ -78,6 +78,13 @@ struct xe_uc_fw {
|
||||
const char *path;
|
||||
/** @user_overridden: user provided path to uC firmware via modparam */
|
||||
bool user_overridden;
|
||||
/**
|
||||
* @full_ver_required: driver still under development and not ready
|
||||
* for backward-compatible firmware. To be used only for **new**
|
||||
* platforms, i.e. still under require_force_probe protection and not
|
||||
* supported by i915.
|
||||
*/
|
||||
bool full_ver_required;
|
||||
/** @size: size of uC firmware including css header */
|
||||
size_t size;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user