> -----Original Message----- > From: De Marchi, Lucas <lucas.demarchi@xxxxxxxxx> > Sent: Thursday, March 23, 2023 10:18 PM > To: intel-xe@xxxxxxxxxxxxxxxxxxxxx > Cc: Srivatsa, Anusha <anusha.srivatsa@xxxxxxxxx>; Harrison, John C > <john.c.harrison@xxxxxxxxx>; Ceraolo Spurio, Daniele > <daniele.ceraolospurio@xxxxxxxxx>; dri-devel@xxxxxxxxxxxxxxxxxxxxx; Daniel > Vetter <daniel.vetter@xxxxxxxx>; Dave Airlie <airlied@xxxxxxxxxx>; De Marchi, > Lucas <lucas.demarchi@xxxxxxxxx> > Subject: [PATCH 3/3] 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 > Extra line ^ > 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 Extra line here. > 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@xxxxxxxxx> > --- > drivers/gpu/drm/xe/xe_uc_fw.c | 315 +++++++++++++++++----------- > drivers/gpu/drm/xe/xe_uc_fw.h | 2 +- > drivers/gpu/drm/xe/xe_uc_fw_types.h | 7 + > 3 files changed, 204 insertions(+), 120 deletions(-) > > diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c > index 174c42873ebb..653bc3584cc5 100644 > --- a/drivers/gpu/drm/xe/xe_uc_fw.c > +++ b/drivers/gpu/drm/xe/xe_uc_fw.c > @@ -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(ALDERLAKE_P, major_ver(i915, guc, adlp, 70, 5)) > \ > + fw_def(ALDERLAKE_S, major_ver(i915, guc, tgl, 70, 5)) > \ > + fw_def(DG2, major_ver(i915, guc, dg2, 70, 5)) > \ > + fw_def(DG1, major_ver(i915, guc, dg1, 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 } Why is b required here? Anusha > +#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(ALDERLAKE_P, guc_def(adlp, 70, 5, 2)) \ > - fw_def(ALDERLAKE_S, guc_def(tgl, 70, 5, 2)) \ > - fw_def(DG2, guc_def(dg2, 70, 5, 2)) \ > - fw_def(DG1, guc_def(dg1, 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; > - > - 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; > + entries = blobs_all[uc_fw->type].entries; > + count = blobs_all[uc_fw->type].count; > + > + 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); > diff --git a/drivers/gpu/drm/xe/xe_uc_fw.h b/drivers/gpu/drm/xe/xe_uc_fw.h > index bf31c3bb0e0f..e16267e71280 100644 > --- a/drivers/gpu/drm/xe/xe_uc_fw.h > +++ b/drivers/gpu/drm/xe/xe_uc_fw.h > @@ -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 > diff --git a/drivers/gpu/drm/xe/xe_uc_fw_types.h > b/drivers/gpu/drm/xe/xe_uc_fw_types.h > index 1cfd30a655df..6c595aaa2399 100644 > --- a/drivers/gpu/drm/xe/xe_uc_fw_types.h > +++ b/drivers/gpu/drm/xe/xe_uc_fw_types.h > @@ -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_version_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; > > -- > 2.39.0