1dd08ebf6SMatthew Brost // SPDX-License-Identifier: MIT 2dd08ebf6SMatthew Brost /* 3dd08ebf6SMatthew Brost * Copyright © 2022 Intel Corporation 4dd08ebf6SMatthew Brost */ 5dd08ebf6SMatthew Brost 6dd08ebf6SMatthew Brost #include <linux/bitfield.h> 7dd08ebf6SMatthew Brost #include <linux/firmware.h> 8dd08ebf6SMatthew Brost 9dd08ebf6SMatthew Brost #include <drm/drm_managed.h> 10dd08ebf6SMatthew Brost 11a9b1a136SLucas De Marchi #include "regs/xe_guc_regs.h" 12dd08ebf6SMatthew Brost #include "xe_bo.h" 13dd08ebf6SMatthew Brost #include "xe_device_types.h" 14dd08ebf6SMatthew Brost #include "xe_force_wake.h" 15dd08ebf6SMatthew Brost #include "xe_gt.h" 16dd08ebf6SMatthew Brost #include "xe_map.h" 17dd08ebf6SMatthew Brost #include "xe_mmio.h" 18a455ed04SDaniele Ceraolo Spurio #include "xe_module.h" 19dd08ebf6SMatthew Brost #include "xe_uc_fw.h" 20dd08ebf6SMatthew Brost 21ad55ead7SLucas De Marchi /* 22ad55ead7SLucas De Marchi * List of required GuC and HuC binaries per-platform. They must be ordered 23ad55ead7SLucas De Marchi * based on platform, from newer to older. 24ad55ead7SLucas De Marchi * 25ad55ead7SLucas De Marchi * Versioning follows the guidelines from 26ad55ead7SLucas De Marchi * Documentation/driver-api/firmware/firmware-usage-guidelines.rst. There is a 27ad55ead7SLucas De Marchi * distinction for platforms being officially supported by the driver or not. 28ad55ead7SLucas De Marchi * Platforms not available publicly or not yet officially supported by the 29ad55ead7SLucas De Marchi * driver (under force-probe), use the mmp_ver(): the firmware autoselect logic 30ad55ead7SLucas De Marchi * will select the firmware from disk with filename that matches the full 31ad55ead7SLucas De Marchi * "mpp version", i.e. major.minor.patch. mmp_ver() should only be used for 32ad55ead7SLucas De Marchi * this case. 33ad55ead7SLucas De Marchi * 34ad55ead7SLucas De Marchi * For platforms officially supported by the driver, the filename always only 35ad55ead7SLucas De Marchi * ever contains the major version (GuC) or no version at all (HuC). 36ad55ead7SLucas De Marchi * 37ad55ead7SLucas De Marchi * After loading the file, the driver parses the versions embedded in the blob. 38ad55ead7SLucas De Marchi * The major version needs to match a major version supported by the driver (if 39ad55ead7SLucas De Marchi * any). The minor version is also checked and a notice emitted to the log if 40ad55ead7SLucas De Marchi * the version found is smaller than the version wanted. This is done only for 41ad55ead7SLucas De Marchi * informational purposes so users may have a chance to upgrade, but the driver 42ad55ead7SLucas De Marchi * still loads and use the older firmware. 43ad55ead7SLucas De Marchi * 44ad55ead7SLucas De Marchi * Examples: 45ad55ead7SLucas De Marchi * 46ad55ead7SLucas De Marchi * 1) Platform officially supported by i915 - using Tigerlake as example. 47ad55ead7SLucas De Marchi * Driver loads the following firmware blobs from disk: 48ad55ead7SLucas De Marchi * 49ad55ead7SLucas De Marchi * - i915/tgl_guc_<major>.bin 50ad55ead7SLucas De Marchi * - i915/tgl_huc.bin 51ad55ead7SLucas De Marchi * 52ad55ead7SLucas De Marchi * <major> number for GuC is checked that it matches the version inside 53ad55ead7SLucas De Marchi * the blob. <minor> version is checked and if smaller than the expected 54ad55ead7SLucas De Marchi * an info message is emitted about that. 55ad55ead7SLucas De Marchi * 56ad55ead7SLucas De Marchi * 1) XE_<FUTUREINTELPLATFORM>, still under require_force_probe. Using 57ad55ead7SLucas De Marchi * "wipplat" as a short-name. Driver loads the following firmware blobs 58ad55ead7SLucas De Marchi * from disk: 59ad55ead7SLucas De Marchi * 60ad55ead7SLucas De Marchi * - xe/wipplat_guc_<major>.<minor>.<patch>.bin 61ad55ead7SLucas De Marchi * - xe/wipplat_huc_<major>.<minor>.<patch>.bin 62ad55ead7SLucas De Marchi * 63ad55ead7SLucas De Marchi * <major> and <minor> are checked that they match the version inside 64ad55ead7SLucas De Marchi * the blob. Both of them need to match exactly what the driver is 65ad55ead7SLucas De Marchi * expecting, otherwise it fails. 66ad55ead7SLucas De Marchi * 67ad55ead7SLucas De Marchi * 3) Platform officially supported by xe and out of force-probe. Using 68ad55ead7SLucas De Marchi * "plat" as a short-name. Except for the different directory, the 69ad55ead7SLucas De Marchi * behavior is the same as (1). Driver loads the following firmware 70ad55ead7SLucas De Marchi * blobs from disk: 71ad55ead7SLucas De Marchi * 72ad55ead7SLucas De Marchi * - xe/plat_guc_<major>.bin 73ad55ead7SLucas De Marchi * - xe/plat_huc.bin 74ad55ead7SLucas De Marchi * 75ad55ead7SLucas De Marchi * <major> number for GuC is checked that it matches the version inside 76ad55ead7SLucas De Marchi * the blob. <minor> version is checked and if smaller than the expected 77ad55ead7SLucas De Marchi * an info message is emitted about that. 78ad55ead7SLucas De Marchi * 79ad55ead7SLucas De Marchi * For the platforms already released with a major version, they should never be 80ad55ead7SLucas De Marchi * removed from the table. Instead new entries with newer versions may be added 81ad55ead7SLucas De Marchi * before them, so they take precedence. 82ad55ead7SLucas De Marchi * 83ad55ead7SLucas De Marchi * TODO: Currently there's no fallback on major version. That's because xe 84ad55ead7SLucas De Marchi * driver only supports the one major version of each firmware in the table. 85ad55ead7SLucas De Marchi * This needs to be fixed when the major version of GuC is updated. 86ad55ead7SLucas De Marchi */ 87ad55ead7SLucas De Marchi 88ad55ead7SLucas De Marchi struct uc_fw_entry { 89ad55ead7SLucas De Marchi enum xe_platform platform; 90ad55ead7SLucas De Marchi struct { 91ad55ead7SLucas De Marchi const char *path; 92ad55ead7SLucas De Marchi u16 major; 93ad55ead7SLucas De Marchi u16 minor; 94ad55ead7SLucas De Marchi bool full_ver_required; 95ad55ead7SLucas De Marchi }; 96ad55ead7SLucas De Marchi }; 97ad55ead7SLucas De Marchi 98ad55ead7SLucas De Marchi struct fw_blobs_by_type { 99ad55ead7SLucas De Marchi const struct uc_fw_entry *entries; 100ad55ead7SLucas De Marchi u32 count; 101ad55ead7SLucas De Marchi }; 102ad55ead7SLucas De Marchi 103ad55ead7SLucas De Marchi #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ 104943c01b7SMatt Roper fw_def(LUNARLAKE, mmp_ver(xe, guc, lnl, 70, 6, 8)) \ 105430003b8SDaniele Ceraolo Spurio fw_def(METEORLAKE, major_ver(i915, guc, mtl, 70, 7)) \ 1069b497627SDaniele Ceraolo Spurio fw_def(PVC, mmp_ver(xe, guc, pvc, 70, 9, 1)) \ 107ad55ead7SLucas De Marchi fw_def(DG2, major_ver(i915, guc, dg2, 70, 5)) \ 108ad55ead7SLucas De Marchi fw_def(DG1, major_ver(i915, guc, dg1, 70, 5)) \ 109500f9062SMatt Roper fw_def(ALDERLAKE_N, major_ver(i915, guc, tgl, 70, 5)) \ 110ad55ead7SLucas De Marchi fw_def(ALDERLAKE_P, major_ver(i915, guc, adlp, 70, 5)) \ 111ad55ead7SLucas De Marchi fw_def(ALDERLAKE_S, major_ver(i915, guc, tgl, 70, 5)) \ 11294324e6bSAnusha Srivatsa fw_def(ROCKETLAKE, major_ver(i915, guc, tgl, 70, 5)) \ 113ad55ead7SLucas De Marchi fw_def(TIGERLAKE, major_ver(i915, guc, tgl, 70, 5)) 114ad55ead7SLucas De Marchi 115ad55ead7SLucas De Marchi #define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver) \ 116bfeb4ac5SDaniele Ceraolo Spurio fw_def(METEORLAKE, no_ver(i915, huc_gsc, mtl)) \ 117420c6a6fSDaniele Ceraolo Spurio fw_def(DG1, no_ver(i915, huc, dg1)) \ 11885635f5dSLucas De Marchi fw_def(ALDERLAKE_P, no_ver(i915, huc, tgl)) \ 119ad55ead7SLucas De Marchi fw_def(ALDERLAKE_S, no_ver(i915, huc, tgl)) \ 12094324e6bSAnusha Srivatsa fw_def(ROCKETLAKE, no_ver(i915, huc, tgl)) \ 121ad55ead7SLucas De Marchi fw_def(TIGERLAKE, no_ver(i915, huc, tgl)) 122ad55ead7SLucas De Marchi 123ad55ead7SLucas De Marchi #define MAKE_FW_PATH(dir__, uc__, shortname__, version__) \ 124ad55ead7SLucas De Marchi __stringify(dir__) "/" __stringify(shortname__) "_" __stringify(uc__) version__ ".bin" 125ad55ead7SLucas De Marchi 126ad55ead7SLucas De Marchi #define fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c) \ 127ad55ead7SLucas De Marchi MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a ## . ## b ## . ## c)) 128ad55ead7SLucas De Marchi #define fw_filename_major_ver(dir_, uc_, shortname_, a, b) \ 129ad55ead7SLucas De Marchi MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a)) 130ad55ead7SLucas De Marchi #define fw_filename_no_ver(dir_, uc_, shortname_) \ 131ad55ead7SLucas De Marchi MAKE_FW_PATH(dir_, uc_, shortname_, "") 132ad55ead7SLucas De Marchi 133ad55ead7SLucas De Marchi #define uc_fw_entry_mmp_ver(dir_, uc_, shortname_, a, b, c) \ 134ad55ead7SLucas De Marchi { fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c), \ 135ad55ead7SLucas De Marchi a, b, true } 136ad55ead7SLucas De Marchi #define uc_fw_entry_major_ver(dir_, uc_, shortname_, a, b) \ 137ad55ead7SLucas De Marchi { fw_filename_major_ver(dir_, uc_, shortname_, a, b), \ 138ad55ead7SLucas De Marchi a, b } 139ad55ead7SLucas De Marchi #define uc_fw_entry_no_ver(dir_, uc_, shortname_) \ 140ad55ead7SLucas De Marchi { fw_filename_no_ver(dir_, uc_, shortname_), \ 141ad55ead7SLucas De Marchi 0, 0 } 142ad55ead7SLucas De Marchi 143ad55ead7SLucas De Marchi /* All blobs need to be declared via MODULE_FIRMWARE() */ 144ad55ead7SLucas De Marchi #define XE_UC_MODULE_FIRMWARE(platform__, fw_filename) \ 145ad55ead7SLucas De Marchi MODULE_FIRMWARE(fw_filename); 146ad55ead7SLucas De Marchi 147ad55ead7SLucas De Marchi #define XE_UC_FW_ENTRY(platform__, entry__) \ 148ad55ead7SLucas De Marchi { \ 149ad55ead7SLucas De Marchi .platform = XE_ ## platform__, \ 150ad55ead7SLucas De Marchi entry__, \ 151ad55ead7SLucas De Marchi }, 152ad55ead7SLucas De Marchi 1533e8e7ee6SFrancois Dugast XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, 154ad55ead7SLucas De Marchi fw_filename_mmp_ver, fw_filename_major_ver) 1553e8e7ee6SFrancois Dugast XE_HUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, 156ad55ead7SLucas De Marchi fw_filename_mmp_ver, fw_filename_no_ver) 157ad55ead7SLucas De Marchi 158dd08ebf6SMatthew Brost static struct xe_gt * 159dd08ebf6SMatthew Brost __uc_fw_to_gt(struct xe_uc_fw *uc_fw, enum xe_uc_fw_type type) 160dd08ebf6SMatthew Brost { 161dd08ebf6SMatthew Brost if (type == XE_UC_FW_TYPE_GUC) 162dd08ebf6SMatthew Brost return container_of(uc_fw, struct xe_gt, uc.guc.fw); 163dd08ebf6SMatthew Brost 16499fea682SFrancois Dugast XE_WARN_ON(type != XE_UC_FW_TYPE_HUC); 165dd08ebf6SMatthew Brost return container_of(uc_fw, struct xe_gt, uc.huc.fw); 166dd08ebf6SMatthew Brost } 167dd08ebf6SMatthew Brost 168dd08ebf6SMatthew Brost static struct xe_gt *uc_fw_to_gt(struct xe_uc_fw *uc_fw) 169dd08ebf6SMatthew Brost { 170dd08ebf6SMatthew Brost return __uc_fw_to_gt(uc_fw, uc_fw->type); 171dd08ebf6SMatthew Brost } 172dd08ebf6SMatthew Brost 173dd08ebf6SMatthew Brost static struct xe_device *uc_fw_to_xe(struct xe_uc_fw *uc_fw) 174dd08ebf6SMatthew Brost { 175dd08ebf6SMatthew Brost return gt_to_xe(uc_fw_to_gt(uc_fw)); 176dd08ebf6SMatthew Brost } 177dd08ebf6SMatthew Brost 178dd08ebf6SMatthew Brost static void 179dd08ebf6SMatthew Brost uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw) 180dd08ebf6SMatthew Brost { 181ad55ead7SLucas De Marchi static const struct uc_fw_entry entries_guc[] = { 182ad55ead7SLucas De Marchi XE_GUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY, 183ad55ead7SLucas De Marchi uc_fw_entry_mmp_ver, 184ad55ead7SLucas De Marchi uc_fw_entry_major_ver) 185dd08ebf6SMatthew Brost }; 186ad55ead7SLucas De Marchi static const struct uc_fw_entry entries_huc[] = { 187ad55ead7SLucas De Marchi XE_HUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY, 188ad55ead7SLucas De Marchi uc_fw_entry_mmp_ver, 189ad55ead7SLucas De Marchi uc_fw_entry_no_ver) 190dd08ebf6SMatthew Brost }; 191dd08ebf6SMatthew Brost static const struct fw_blobs_by_type blobs_all[XE_UC_FW_NUM_TYPES] = { 192ad55ead7SLucas De Marchi [XE_UC_FW_TYPE_GUC] = { entries_guc, ARRAY_SIZE(entries_guc) }, 193ad55ead7SLucas De Marchi [XE_UC_FW_TYPE_HUC] = { entries_huc, ARRAY_SIZE(entries_huc) }, 194dd08ebf6SMatthew Brost }; 195ad55ead7SLucas De Marchi static const struct uc_fw_entry *entries; 196dd08ebf6SMatthew Brost enum xe_platform p = xe->info.platform; 197ad55ead7SLucas De Marchi u32 count; 198dd08ebf6SMatthew Brost int i; 199dd08ebf6SMatthew Brost 200c73acc1eSFrancois Dugast xe_assert(xe, uc_fw->type < ARRAY_SIZE(blobs_all)); 201ad55ead7SLucas De Marchi entries = blobs_all[uc_fw->type].entries; 202ad55ead7SLucas De Marchi count = blobs_all[uc_fw->type].count; 203dd08ebf6SMatthew Brost 204ad55ead7SLucas De Marchi for (i = 0; i < count && p <= entries[i].platform; i++) { 205ad55ead7SLucas De Marchi if (p == entries[i].platform) { 206ad55ead7SLucas De Marchi uc_fw->path = entries[i].path; 207*2e7227b4SDaniele Ceraolo Spurio uc_fw->versions.wanted.major = entries[i].major; 208*2e7227b4SDaniele Ceraolo Spurio uc_fw->versions.wanted.minor = entries[i].minor; 209ad55ead7SLucas De Marchi uc_fw->full_ver_required = entries[i].full_ver_required; 210*2e7227b4SDaniele Ceraolo Spurio 211*2e7227b4SDaniele Ceraolo Spurio /* compatibility version checking coming soon */ 212*2e7227b4SDaniele Ceraolo Spurio uc_fw->versions.wanted_type = XE_UC_FW_VER_RELEASE; 213dd08ebf6SMatthew Brost break; 214dd08ebf6SMatthew Brost } 215dd08ebf6SMatthew Brost } 216dd08ebf6SMatthew Brost } 217dd08ebf6SMatthew Brost 218a455ed04SDaniele Ceraolo Spurio static void 219a455ed04SDaniele Ceraolo Spurio uc_fw_override(struct xe_uc_fw *uc_fw) 220a455ed04SDaniele Ceraolo Spurio { 221a455ed04SDaniele Ceraolo Spurio char *path_override = NULL; 222a455ed04SDaniele Ceraolo Spurio 223a455ed04SDaniele Ceraolo Spurio /* empty string disables, but it's not allowed for GuC */ 224a455ed04SDaniele Ceraolo Spurio switch (uc_fw->type) { 225a455ed04SDaniele Ceraolo Spurio case XE_UC_FW_TYPE_GUC: 226adce1b39SBommithi Sakeena if (xe_modparam.guc_firmware_path && *xe_modparam.guc_firmware_path) 227adce1b39SBommithi Sakeena path_override = xe_modparam.guc_firmware_path; 228a455ed04SDaniele Ceraolo Spurio break; 229a455ed04SDaniele Ceraolo Spurio case XE_UC_FW_TYPE_HUC: 230adce1b39SBommithi Sakeena path_override = xe_modparam.huc_firmware_path; 231a455ed04SDaniele Ceraolo Spurio break; 232a455ed04SDaniele Ceraolo Spurio default: 233a455ed04SDaniele Ceraolo Spurio break; 234a455ed04SDaniele Ceraolo Spurio } 235a455ed04SDaniele Ceraolo Spurio 236a455ed04SDaniele Ceraolo Spurio if (path_override) { 237a455ed04SDaniele Ceraolo Spurio uc_fw->path = path_override; 238a455ed04SDaniele Ceraolo Spurio uc_fw->user_overridden = true; 239a455ed04SDaniele Ceraolo Spurio } 240a455ed04SDaniele Ceraolo Spurio } 241a455ed04SDaniele Ceraolo Spurio 242dd08ebf6SMatthew Brost /** 243dd08ebf6SMatthew Brost * xe_uc_fw_copy_rsa - copy fw RSA to buffer 244dd08ebf6SMatthew Brost * 245dd08ebf6SMatthew Brost * @uc_fw: uC firmware 246dd08ebf6SMatthew Brost * @dst: dst buffer 247dd08ebf6SMatthew Brost * @max_len: max number of bytes to copy 248dd08ebf6SMatthew Brost * 249dd08ebf6SMatthew Brost * Return: number of copied bytes. 250dd08ebf6SMatthew Brost */ 251dd08ebf6SMatthew Brost size_t xe_uc_fw_copy_rsa(struct xe_uc_fw *uc_fw, void *dst, u32 max_len) 252dd08ebf6SMatthew Brost { 253dd08ebf6SMatthew Brost struct xe_device *xe = uc_fw_to_xe(uc_fw); 254dd08ebf6SMatthew Brost u32 size = min_t(u32, uc_fw->rsa_size, max_len); 255dd08ebf6SMatthew Brost 256c73acc1eSFrancois Dugast xe_assert(xe, !(size % 4)); 257c73acc1eSFrancois Dugast xe_assert(xe, xe_uc_fw_is_available(uc_fw)); 258dd08ebf6SMatthew Brost 259dd08ebf6SMatthew Brost xe_map_memcpy_from(xe, dst, &uc_fw->bo->vmap, 260dd08ebf6SMatthew Brost xe_uc_fw_rsa_offset(uc_fw), size); 261dd08ebf6SMatthew Brost 262dd08ebf6SMatthew Brost return size; 263dd08ebf6SMatthew Brost } 264dd08ebf6SMatthew Brost 265dd08ebf6SMatthew Brost static void uc_fw_fini(struct drm_device *drm, void *arg) 266dd08ebf6SMatthew Brost { 267dd08ebf6SMatthew Brost struct xe_uc_fw *uc_fw = arg; 268dd08ebf6SMatthew Brost 269dd08ebf6SMatthew Brost if (!xe_uc_fw_is_available(uc_fw)) 270dd08ebf6SMatthew Brost return; 271dd08ebf6SMatthew Brost 272dd08ebf6SMatthew Brost xe_bo_unpin_map_no_vm(uc_fw->bo); 273dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_SELECTED); 274dd08ebf6SMatthew Brost } 275dd08ebf6SMatthew Brost 27699c821b0SMatthew Brost static void guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css) 27799c821b0SMatthew Brost { 27899c821b0SMatthew Brost struct xe_gt *gt = uc_fw_to_gt(uc_fw); 279*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE]; 280*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *compatibility = &uc_fw->versions.found[XE_UC_FW_VER_COMPATIBILITY]; 28199c821b0SMatthew Brost 282c73acc1eSFrancois Dugast xe_gt_assert(gt, uc_fw->type == XE_UC_FW_TYPE_GUC); 283*2e7227b4SDaniele Ceraolo Spurio xe_gt_assert(gt, release->major >= 70); 28499c821b0SMatthew Brost 285*2e7227b4SDaniele Ceraolo Spurio if (release->major > 70 || release->minor >= 6) { 28699c821b0SMatthew Brost /* v70.6.0 adds CSS header support */ 287*2e7227b4SDaniele Ceraolo Spurio compatibility->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, 28899c821b0SMatthew Brost css->submission_version); 289*2e7227b4SDaniele Ceraolo Spurio compatibility->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, 29099c821b0SMatthew Brost css->submission_version); 291*2e7227b4SDaniele Ceraolo Spurio compatibility->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, 29299c821b0SMatthew Brost css->submission_version); 293*2e7227b4SDaniele Ceraolo Spurio } else if (release->minor >= 3) { 29499c821b0SMatthew Brost /* v70.3.0 introduced v1.1.0 */ 295*2e7227b4SDaniele Ceraolo Spurio compatibility->major = 1; 296*2e7227b4SDaniele Ceraolo Spurio compatibility->minor = 1; 297*2e7227b4SDaniele Ceraolo Spurio compatibility->patch = 0; 29899c821b0SMatthew Brost } else { 29999c821b0SMatthew Brost /* v70.0.0 introduced v1.0.0 */ 300*2e7227b4SDaniele Ceraolo Spurio compatibility->major = 1; 301*2e7227b4SDaniele Ceraolo Spurio compatibility->minor = 0; 302*2e7227b4SDaniele Ceraolo Spurio compatibility->patch = 0; 30399c821b0SMatthew Brost } 30499c821b0SMatthew Brost 30599c821b0SMatthew Brost uc_fw->private_data_size = css->private_data_size; 30699c821b0SMatthew Brost } 30799c821b0SMatthew Brost 308ad55ead7SLucas De Marchi static int uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw) 309ad55ead7SLucas De Marchi { 310ad55ead7SLucas De Marchi struct xe_device *xe = uc_fw_to_xe(uc_fw); 311*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted; 312*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *found = &uc_fw->versions.found[uc_fw->versions.wanted_type]; 313ad55ead7SLucas De Marchi 314ad55ead7SLucas De Marchi /* Driver has no requirement on any version, any is good. */ 315*2e7227b4SDaniele Ceraolo Spurio if (!wanted->major) 316ad55ead7SLucas De Marchi return 0; 317ad55ead7SLucas De Marchi 318ad55ead7SLucas De Marchi /* 319ad55ead7SLucas De Marchi * If full version is required, both major and minor should match. 320ad55ead7SLucas De Marchi * Otherwise, at least the major version. 321ad55ead7SLucas De Marchi */ 322*2e7227b4SDaniele Ceraolo Spurio if (wanted->major != found->major || 323*2e7227b4SDaniele Ceraolo Spurio (uc_fw->full_ver_required && wanted->minor != found->minor)) { 324ad55ead7SLucas De Marchi drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u != %u.%u\n", 325ad55ead7SLucas De Marchi xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, 326*2e7227b4SDaniele Ceraolo Spurio found->major, found->minor, 327*2e7227b4SDaniele Ceraolo Spurio wanted->major, wanted->minor); 328ad55ead7SLucas De Marchi goto fail; 329ad55ead7SLucas De Marchi } 330ad55ead7SLucas De Marchi 331*2e7227b4SDaniele Ceraolo Spurio if (wanted->minor > found->minor) { 332ad55ead7SLucas De Marchi drm_notice(&xe->drm, "%s firmware (%u.%u) is recommended, but only (%u.%u) was found in %s\n", 333ad55ead7SLucas De Marchi xe_uc_fw_type_repr(uc_fw->type), 334*2e7227b4SDaniele Ceraolo Spurio wanted->major, wanted->minor, 335*2e7227b4SDaniele Ceraolo Spurio found->major, found->minor, 336ad55ead7SLucas De Marchi uc_fw->path); 337ad55ead7SLucas De Marchi drm_info(&xe->drm, "Consider updating your linux-firmware pkg or downloading from %s\n", 338ad55ead7SLucas De Marchi XE_UC_FIRMWARE_URL); 339ad55ead7SLucas De Marchi } 340ad55ead7SLucas De Marchi 341ad55ead7SLucas De Marchi return 0; 342ad55ead7SLucas De Marchi 343ad55ead7SLucas De Marchi fail: 344ad55ead7SLucas De Marchi if (xe_uc_fw_is_overridden(uc_fw)) 345ad55ead7SLucas De Marchi return 0; 346ad55ead7SLucas De Marchi 347ad55ead7SLucas De Marchi return -ENOEXEC; 348ad55ead7SLucas De Marchi } 349ad55ead7SLucas De Marchi 350a9a95523SDaniele Ceraolo Spurio /* Refer to the "CSS-based Firmware Layout" documentation entry for details */ 351a9a95523SDaniele Ceraolo Spurio static int parse_css_header(struct xe_uc_fw *uc_fw, const void *fw_data, size_t fw_size) 352a9a95523SDaniele Ceraolo Spurio { 353a9a95523SDaniele Ceraolo Spurio struct xe_device *xe = uc_fw_to_xe(uc_fw); 354*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE]; 355a9a95523SDaniele Ceraolo Spurio struct uc_css_header *css; 356a9a95523SDaniele Ceraolo Spurio size_t size; 357a9a95523SDaniele Ceraolo Spurio 358a9a95523SDaniele Ceraolo Spurio /* Check the size of the blob before examining buffer contents */ 359a9a95523SDaniele Ceraolo Spurio if (unlikely(fw_size < sizeof(struct uc_css_header))) { 360a9a95523SDaniele Ceraolo Spurio drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n", 361a9a95523SDaniele Ceraolo Spurio xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, 362a9a95523SDaniele Ceraolo Spurio fw_size, sizeof(struct uc_css_header)); 363a9a95523SDaniele Ceraolo Spurio return -ENODATA; 364a9a95523SDaniele Ceraolo Spurio } 365a9a95523SDaniele Ceraolo Spurio 366a9a95523SDaniele Ceraolo Spurio css = (struct uc_css_header *)fw_data; 367a9a95523SDaniele Ceraolo Spurio 368a9a95523SDaniele Ceraolo Spurio /* Check integrity of size values inside CSS header */ 369a9a95523SDaniele Ceraolo Spurio size = (css->header_size_dw - css->key_size_dw - css->modulus_size_dw - 370a9a95523SDaniele Ceraolo Spurio css->exponent_size_dw) * sizeof(u32); 371a9a95523SDaniele Ceraolo Spurio if (unlikely(size != sizeof(struct uc_css_header))) { 372a9a95523SDaniele Ceraolo Spurio drm_warn(&xe->drm, 373a9a95523SDaniele Ceraolo Spurio "%s firmware %s: unexpected header size: %zu != %zu\n", 374a9a95523SDaniele Ceraolo Spurio xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, 375a9a95523SDaniele Ceraolo Spurio fw_size, sizeof(struct uc_css_header)); 376a9a95523SDaniele Ceraolo Spurio return -EPROTO; 377a9a95523SDaniele Ceraolo Spurio } 378a9a95523SDaniele Ceraolo Spurio 379a9a95523SDaniele Ceraolo Spurio /* uCode size must calculated from other sizes */ 380a9a95523SDaniele Ceraolo Spurio uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); 381a9a95523SDaniele Ceraolo Spurio 382a9a95523SDaniele Ceraolo Spurio /* now RSA */ 383a9a95523SDaniele Ceraolo Spurio uc_fw->rsa_size = css->key_size_dw * sizeof(u32); 384a9a95523SDaniele Ceraolo Spurio 385a9a95523SDaniele Ceraolo Spurio /* At least, it should have header, uCode and RSA. Size of all three. */ 386a9a95523SDaniele Ceraolo Spurio size = sizeof(struct uc_css_header) + uc_fw->ucode_size + 387a9a95523SDaniele Ceraolo Spurio uc_fw->rsa_size; 388a9a95523SDaniele Ceraolo Spurio if (unlikely(fw_size < size)) { 389a9a95523SDaniele Ceraolo Spurio drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n", 390a9a95523SDaniele Ceraolo Spurio xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, 391a9a95523SDaniele Ceraolo Spurio fw_size, size); 392a9a95523SDaniele Ceraolo Spurio return -ENOEXEC; 393a9a95523SDaniele Ceraolo Spurio } 394a9a95523SDaniele Ceraolo Spurio 395a9a95523SDaniele Ceraolo Spurio /* Get version numbers from the CSS header */ 396*2e7227b4SDaniele Ceraolo Spurio release->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css->sw_version); 397*2e7227b4SDaniele Ceraolo Spurio release->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css->sw_version); 398*2e7227b4SDaniele Ceraolo Spurio release->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css->sw_version); 399a9a95523SDaniele Ceraolo Spurio 400a9a95523SDaniele Ceraolo Spurio if (uc_fw->type == XE_UC_FW_TYPE_GUC) 401a9a95523SDaniele Ceraolo Spurio guc_read_css_info(uc_fw, css); 402a9a95523SDaniele Ceraolo Spurio 403a9a95523SDaniele Ceraolo Spurio return 0; 404a9a95523SDaniele Ceraolo Spurio } 405a9a95523SDaniele Ceraolo Spurio 406484ecffaSDaniele Ceraolo Spurio static bool is_cpd_header(const void *data) 407484ecffaSDaniele Ceraolo Spurio { 408484ecffaSDaniele Ceraolo Spurio const u32 *marker = data; 409484ecffaSDaniele Ceraolo Spurio 410484ecffaSDaniele Ceraolo Spurio return *marker == GSC_CPD_HEADER_MARKER; 411484ecffaSDaniele Ceraolo Spurio } 412484ecffaSDaniele Ceraolo Spurio 413484ecffaSDaniele Ceraolo Spurio static u32 entry_offset(const struct gsc_cpd_header_v2 *header, const char *name) 414484ecffaSDaniele Ceraolo Spurio { 415484ecffaSDaniele Ceraolo Spurio const struct gsc_cpd_entry *entry; 416484ecffaSDaniele Ceraolo Spurio int i; 417484ecffaSDaniele Ceraolo Spurio 418484ecffaSDaniele Ceraolo Spurio entry = (void *)header + header->header_length; 419484ecffaSDaniele Ceraolo Spurio 420484ecffaSDaniele Ceraolo Spurio for (i = 0; i < header->num_of_entries; i++, entry++) 421484ecffaSDaniele Ceraolo Spurio if (strcmp(entry->name, name) == 0) 422484ecffaSDaniele Ceraolo Spurio return entry->offset & GSC_CPD_ENTRY_OFFSET_MASK; 423484ecffaSDaniele Ceraolo Spurio 424484ecffaSDaniele Ceraolo Spurio return 0; 425484ecffaSDaniele Ceraolo Spurio } 426484ecffaSDaniele Ceraolo Spurio 427484ecffaSDaniele Ceraolo Spurio /* Refer to the "GSC-based Firmware Layout" documentation entry for details */ 428484ecffaSDaniele Ceraolo Spurio static int parse_cpd_header(struct xe_uc_fw *uc_fw, const void *data, size_t size, 429484ecffaSDaniele Ceraolo Spurio const char *manifest_entry, const char *css_entry) 430484ecffaSDaniele Ceraolo Spurio { 431484ecffaSDaniele Ceraolo Spurio struct xe_gt *gt = uc_fw_to_gt(uc_fw); 432484ecffaSDaniele Ceraolo Spurio struct xe_device *xe = gt_to_xe(gt); 433484ecffaSDaniele Ceraolo Spurio const struct gsc_cpd_header_v2 *header = data; 434*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE]; 435484ecffaSDaniele Ceraolo Spurio const struct gsc_manifest_header *manifest; 436484ecffaSDaniele Ceraolo Spurio size_t min_size = sizeof(*header); 437484ecffaSDaniele Ceraolo Spurio u32 offset; 438484ecffaSDaniele Ceraolo Spurio 439484ecffaSDaniele Ceraolo Spurio /* manifest_entry is mandatory, css_entry is optional */ 440484ecffaSDaniele Ceraolo Spurio xe_assert(xe, manifest_entry); 441484ecffaSDaniele Ceraolo Spurio 442484ecffaSDaniele Ceraolo Spurio if (size < min_size || !is_cpd_header(header)) 443484ecffaSDaniele Ceraolo Spurio return -ENOENT; 444484ecffaSDaniele Ceraolo Spurio 445484ecffaSDaniele Ceraolo Spurio if (header->header_length < sizeof(struct gsc_cpd_header_v2)) { 446484ecffaSDaniele Ceraolo Spurio xe_gt_err(gt, "invalid CPD header length %u!\n", header->header_length); 447484ecffaSDaniele Ceraolo Spurio return -EINVAL; 448484ecffaSDaniele Ceraolo Spurio } 449484ecffaSDaniele Ceraolo Spurio 450484ecffaSDaniele Ceraolo Spurio min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries; 451484ecffaSDaniele Ceraolo Spurio if (size < min_size) { 452484ecffaSDaniele Ceraolo Spurio xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size); 453484ecffaSDaniele Ceraolo Spurio return -ENODATA; 454484ecffaSDaniele Ceraolo Spurio } 455484ecffaSDaniele Ceraolo Spurio 456484ecffaSDaniele Ceraolo Spurio /* Look for the manifest first */ 457484ecffaSDaniele Ceraolo Spurio offset = entry_offset(header, manifest_entry); 458484ecffaSDaniele Ceraolo Spurio if (!offset) { 459484ecffaSDaniele Ceraolo Spurio xe_gt_err(gt, "Failed to find %s manifest!\n", 460484ecffaSDaniele Ceraolo Spurio xe_uc_fw_type_repr(uc_fw->type)); 461484ecffaSDaniele Ceraolo Spurio return -ENODATA; 462484ecffaSDaniele Ceraolo Spurio } 463484ecffaSDaniele Ceraolo Spurio 464484ecffaSDaniele Ceraolo Spurio min_size = offset + sizeof(struct gsc_manifest_header); 465484ecffaSDaniele Ceraolo Spurio if (size < min_size) { 466484ecffaSDaniele Ceraolo Spurio xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size); 467484ecffaSDaniele Ceraolo Spurio return -ENODATA; 468484ecffaSDaniele Ceraolo Spurio } 469484ecffaSDaniele Ceraolo Spurio 470484ecffaSDaniele Ceraolo Spurio manifest = data + offset; 471484ecffaSDaniele Ceraolo Spurio 472*2e7227b4SDaniele Ceraolo Spurio release->major = manifest->fw_version.major; 473*2e7227b4SDaniele Ceraolo Spurio release->minor = manifest->fw_version.minor; 474*2e7227b4SDaniele Ceraolo Spurio release->patch = manifest->fw_version.hotfix; 475484ecffaSDaniele Ceraolo Spurio 476484ecffaSDaniele Ceraolo Spurio /* then optionally look for the css header */ 477484ecffaSDaniele Ceraolo Spurio if (css_entry) { 478484ecffaSDaniele Ceraolo Spurio int ret; 479484ecffaSDaniele Ceraolo Spurio 480484ecffaSDaniele Ceraolo Spurio /* 481484ecffaSDaniele Ceraolo Spurio * This section does not contain a CSS entry on DG2. We 482484ecffaSDaniele Ceraolo Spurio * don't support DG2 HuC right now, so no need to handle 483484ecffaSDaniele Ceraolo Spurio * it, just add a reminder in case that changes. 484484ecffaSDaniele Ceraolo Spurio */ 485484ecffaSDaniele Ceraolo Spurio xe_assert(xe, xe->info.platform != XE_DG2); 486484ecffaSDaniele Ceraolo Spurio 487484ecffaSDaniele Ceraolo Spurio offset = entry_offset(header, css_entry); 488484ecffaSDaniele Ceraolo Spurio 489484ecffaSDaniele Ceraolo Spurio /* the CSS header parser will check that the CSS header fits */ 490484ecffaSDaniele Ceraolo Spurio if (offset > size) { 491484ecffaSDaniele Ceraolo Spurio xe_gt_err(gt, "FW too small! %zu < %u\n", size, offset); 492484ecffaSDaniele Ceraolo Spurio return -ENODATA; 493484ecffaSDaniele Ceraolo Spurio } 494484ecffaSDaniele Ceraolo Spurio 495484ecffaSDaniele Ceraolo Spurio ret = parse_css_header(uc_fw, data + offset, size - offset); 496484ecffaSDaniele Ceraolo Spurio if (ret) 497484ecffaSDaniele Ceraolo Spurio return ret; 498484ecffaSDaniele Ceraolo Spurio 499484ecffaSDaniele Ceraolo Spurio uc_fw->css_offset = offset; 500484ecffaSDaniele Ceraolo Spurio } 501484ecffaSDaniele Ceraolo Spurio 502484ecffaSDaniele Ceraolo Spurio return 0; 503484ecffaSDaniele Ceraolo Spurio } 504484ecffaSDaniele Ceraolo Spurio 505a9a95523SDaniele Ceraolo Spurio static int parse_headers(struct xe_uc_fw *uc_fw, const struct firmware *fw) 506a9a95523SDaniele Ceraolo Spurio { 507484ecffaSDaniele Ceraolo Spurio int ret; 508484ecffaSDaniele Ceraolo Spurio 509484ecffaSDaniele Ceraolo Spurio /* 510484ecffaSDaniele Ceraolo Spurio * All GuC releases and older HuC ones use CSS headers, while newer HuC 511484ecffaSDaniele Ceraolo Spurio * releases use GSC CPD headers. 512484ecffaSDaniele Ceraolo Spurio */ 513484ecffaSDaniele Ceraolo Spurio switch (uc_fw->type) { 514484ecffaSDaniele Ceraolo Spurio case XE_UC_FW_TYPE_HUC: 515484ecffaSDaniele Ceraolo Spurio ret = parse_cpd_header(uc_fw, fw->data, fw->size, "HUCP.man", "huc_fw"); 516484ecffaSDaniele Ceraolo Spurio if (!ret || ret != -ENOENT) 517484ecffaSDaniele Ceraolo Spurio return ret; 518484ecffaSDaniele Ceraolo Spurio fallthrough; 519484ecffaSDaniele Ceraolo Spurio case XE_UC_FW_TYPE_GUC: 520a9a95523SDaniele Ceraolo Spurio return parse_css_header(uc_fw, fw->data, fw->size); 521484ecffaSDaniele Ceraolo Spurio default: 522484ecffaSDaniele Ceraolo Spurio return -EINVAL; 523484ecffaSDaniele Ceraolo Spurio } 524484ecffaSDaniele Ceraolo Spurio 525484ecffaSDaniele Ceraolo Spurio return 0; 526a9a95523SDaniele Ceraolo Spurio } 527a9a95523SDaniele Ceraolo Spurio 528*2e7227b4SDaniele Ceraolo Spurio #define print_uc_fw_version(p_, version_, prefix_, ...) \ 529*2e7227b4SDaniele Ceraolo Spurio do { \ 530*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *ver_ = (version_); \ 531*2e7227b4SDaniele Ceraolo Spurio if (ver_->build) \ 532*2e7227b4SDaniele Ceraolo Spurio drm_printf(p_, prefix_ " version %u.%u.%u.%u\n", ##__VA_ARGS__, \ 533*2e7227b4SDaniele Ceraolo Spurio ver_->major, ver_->minor, \ 534*2e7227b4SDaniele Ceraolo Spurio ver_->patch, ver_->build); \ 535*2e7227b4SDaniele Ceraolo Spurio else \ 536*2e7227b4SDaniele Ceraolo Spurio drm_printf(p_, prefix_ " version %u.%u.%u\n", ##__VA_ARGS__, \ 537*2e7227b4SDaniele Ceraolo Spurio ver_->major, ver_->minor, ver_->patch); \ 538*2e7227b4SDaniele Ceraolo Spurio } while (0) 539*2e7227b4SDaniele Ceraolo Spurio 540dd08ebf6SMatthew Brost int xe_uc_fw_init(struct xe_uc_fw *uc_fw) 541dd08ebf6SMatthew Brost { 542dd08ebf6SMatthew Brost struct xe_device *xe = uc_fw_to_xe(uc_fw); 543dd08ebf6SMatthew Brost struct xe_gt *gt = uc_fw_to_gt(uc_fw); 544876611c2SMatt Roper struct xe_tile *tile = gt_to_tile(gt); 545dd08ebf6SMatthew Brost struct device *dev = xe->drm.dev; 546*2e7227b4SDaniele Ceraolo Spurio struct drm_printer p = drm_info_printer(dev); 547dd08ebf6SMatthew Brost const struct firmware *fw = NULL; 548dd08ebf6SMatthew Brost struct xe_bo *obj; 549dd08ebf6SMatthew Brost int err; 550dd08ebf6SMatthew Brost 551dd08ebf6SMatthew Brost /* 552dd08ebf6SMatthew Brost * we use FIRMWARE_UNINITIALIZED to detect checks against uc_fw->status 553dd08ebf6SMatthew Brost * before we're looked at the HW caps to see if we have uc support 554dd08ebf6SMatthew Brost */ 555dd08ebf6SMatthew Brost BUILD_BUG_ON(XE_UC_FIRMWARE_UNINITIALIZED); 556c73acc1eSFrancois Dugast xe_assert(xe, !uc_fw->status); 557c73acc1eSFrancois Dugast xe_assert(xe, !uc_fw->path); 558dd08ebf6SMatthew Brost 559dd08ebf6SMatthew Brost uc_fw_auto_select(xe, uc_fw); 56075730847SDaniele Ceraolo Spurio xe_uc_fw_change_status(uc_fw, uc_fw->path ? 561dd08ebf6SMatthew Brost XE_UC_FIRMWARE_SELECTED : 562dd08ebf6SMatthew Brost XE_UC_FIRMWARE_NOT_SUPPORTED); 563dd08ebf6SMatthew Brost 56475730847SDaniele Ceraolo Spurio if (!xe_uc_fw_is_supported(uc_fw)) 56575730847SDaniele Ceraolo Spurio return 0; 56675730847SDaniele Ceraolo Spurio 567a455ed04SDaniele Ceraolo Spurio uc_fw_override(uc_fw); 568a455ed04SDaniele Ceraolo Spurio 569a455ed04SDaniele Ceraolo Spurio /* an empty path means the firmware is disabled */ 570a455ed04SDaniele Ceraolo Spurio if (!xe_device_uc_enabled(xe) || !(*uc_fw->path)) { 571dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_DISABLED); 57275730847SDaniele Ceraolo Spurio drm_dbg(&xe->drm, "%s disabled", xe_uc_fw_type_repr(uc_fw->type)); 57375730847SDaniele Ceraolo Spurio return 0; 574dd08ebf6SMatthew Brost } 57575730847SDaniele Ceraolo Spurio 576dd08ebf6SMatthew Brost err = request_firmware(&fw, uc_fw->path, dev); 577dd08ebf6SMatthew Brost if (err) 578dd08ebf6SMatthew Brost goto fail; 579dd08ebf6SMatthew Brost 580a9a95523SDaniele Ceraolo Spurio err = parse_headers(uc_fw, fw); 581a9a95523SDaniele Ceraolo Spurio if (err) 582dd08ebf6SMatthew Brost goto fail; 583dd08ebf6SMatthew Brost 584*2e7227b4SDaniele Ceraolo Spurio print_uc_fw_version(&p, 585*2e7227b4SDaniele Ceraolo Spurio &uc_fw->versions.found[XE_UC_FW_VER_RELEASE], 586*2e7227b4SDaniele Ceraolo Spurio "Using %s firmware from %s", 587*2e7227b4SDaniele Ceraolo Spurio xe_uc_fw_type_repr(uc_fw->type), uc_fw->path); 58861e72e77SLucas De Marchi 589ad55ead7SLucas De Marchi err = uc_fw_check_version_requirements(uc_fw); 590ad55ead7SLucas De Marchi if (err) 591dd08ebf6SMatthew Brost goto fail; 592dd08ebf6SMatthew Brost 593876611c2SMatt Roper obj = xe_bo_create_from_data(xe, tile, fw->data, fw->size, 594dd08ebf6SMatthew Brost ttm_bo_type_kernel, 595876611c2SMatt Roper XE_BO_CREATE_VRAM_IF_DGFX(tile) | 596dd08ebf6SMatthew Brost XE_BO_CREATE_GGTT_BIT); 597dd08ebf6SMatthew Brost if (IS_ERR(obj)) { 598dd08ebf6SMatthew Brost drm_notice(&xe->drm, "%s firmware %s: failed to create / populate bo", 599dd08ebf6SMatthew Brost xe_uc_fw_type_repr(uc_fw->type), uc_fw->path); 600dd08ebf6SMatthew Brost err = PTR_ERR(obj); 601dd08ebf6SMatthew Brost goto fail; 602dd08ebf6SMatthew Brost } 603dd08ebf6SMatthew Brost 604dd08ebf6SMatthew Brost uc_fw->bo = obj; 605dd08ebf6SMatthew Brost uc_fw->size = fw->size; 606dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_AVAILABLE); 607dd08ebf6SMatthew Brost 608dd08ebf6SMatthew Brost release_firmware(fw); 609dd08ebf6SMatthew Brost 610dd08ebf6SMatthew Brost err = drmm_add_action_or_reset(&xe->drm, uc_fw_fini, uc_fw); 611dd08ebf6SMatthew Brost if (err) 612dd08ebf6SMatthew Brost return err; 613dd08ebf6SMatthew Brost 614dd08ebf6SMatthew Brost return 0; 615dd08ebf6SMatthew Brost 616dd08ebf6SMatthew Brost fail: 617dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, err == -ENOENT ? 618dd08ebf6SMatthew Brost XE_UC_FIRMWARE_MISSING : 619dd08ebf6SMatthew Brost XE_UC_FIRMWARE_ERROR); 620dd08ebf6SMatthew Brost 621dd08ebf6SMatthew Brost drm_notice(&xe->drm, "%s firmware %s: fetch failed with error %d\n", 622dd08ebf6SMatthew Brost xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, err); 623dd08ebf6SMatthew Brost drm_info(&xe->drm, "%s firmware(s) can be downloaded from %s\n", 624dd08ebf6SMatthew Brost xe_uc_fw_type_repr(uc_fw->type), XE_UC_FIRMWARE_URL); 625dd08ebf6SMatthew Brost 626dd08ebf6SMatthew Brost release_firmware(fw); /* OK even if fw is NULL */ 627dd08ebf6SMatthew Brost return err; 628dd08ebf6SMatthew Brost } 629dd08ebf6SMatthew Brost 630dd08ebf6SMatthew Brost static u32 uc_fw_ggtt_offset(struct xe_uc_fw *uc_fw) 631dd08ebf6SMatthew Brost { 632dd08ebf6SMatthew Brost return xe_bo_ggtt_addr(uc_fw->bo); 633dd08ebf6SMatthew Brost } 634dd08ebf6SMatthew Brost 635dd08ebf6SMatthew Brost static int uc_fw_xfer(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags) 636dd08ebf6SMatthew Brost { 637dd08ebf6SMatthew Brost struct xe_device *xe = uc_fw_to_xe(uc_fw); 638dd08ebf6SMatthew Brost struct xe_gt *gt = uc_fw_to_gt(uc_fw); 6397aaec3a6SRodrigo Vivi u32 src_offset, dma_ctrl; 640dd08ebf6SMatthew Brost int ret; 641dd08ebf6SMatthew Brost 642dd08ebf6SMatthew Brost xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT); 643dd08ebf6SMatthew Brost 644dd08ebf6SMatthew Brost /* Set the source address for the uCode */ 645484ecffaSDaniele Ceraolo Spurio src_offset = uc_fw_ggtt_offset(uc_fw) + uc_fw->css_offset; 646ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_ADDR_0_LOW, lower_32_bits(src_offset)); 647ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_ADDR_0_HIGH, upper_32_bits(src_offset)); 648dd08ebf6SMatthew Brost 649dd08ebf6SMatthew Brost /* Set the DMA destination */ 650ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_ADDR_1_LOW, offset); 651ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM); 652dd08ebf6SMatthew Brost 653dd08ebf6SMatthew Brost /* 654dd08ebf6SMatthew Brost * Set the transfer size. The header plus uCode will be copied to WOPCM 655dd08ebf6SMatthew Brost * via DMA, excluding any other components 656dd08ebf6SMatthew Brost */ 657ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_COPY_SIZE, 658dd08ebf6SMatthew Brost sizeof(struct uc_css_header) + uc_fw->ucode_size); 659dd08ebf6SMatthew Brost 660dd08ebf6SMatthew Brost /* Start the DMA */ 661ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_CTRL, 662dd08ebf6SMatthew Brost _MASKED_BIT_ENABLE(dma_flags | START_DMA)); 663dd08ebf6SMatthew Brost 664dd08ebf6SMatthew Brost /* Wait for DMA to finish */ 665063e09afSRodrigo Vivi ret = xe_mmio_wait32(gt, DMA_CTRL, START_DMA, 0, 100000, &dma_ctrl, 6667dc9b92dSRodrigo Vivi false); 667dd08ebf6SMatthew Brost if (ret) 668dd08ebf6SMatthew Brost drm_err(&xe->drm, "DMA for %s fw failed, DMA_CTRL=%u\n", 6697aaec3a6SRodrigo Vivi xe_uc_fw_type_repr(uc_fw->type), dma_ctrl); 670dd08ebf6SMatthew Brost 671dd08ebf6SMatthew Brost /* Disable the bits once DMA is over */ 672ce8bf5bdSLucas De Marchi xe_mmio_write32(gt, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags)); 673dd08ebf6SMatthew Brost 674dd08ebf6SMatthew Brost return ret; 675dd08ebf6SMatthew Brost } 676dd08ebf6SMatthew Brost 677dd08ebf6SMatthew Brost int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags) 678dd08ebf6SMatthew Brost { 679dd08ebf6SMatthew Brost struct xe_device *xe = uc_fw_to_xe(uc_fw); 680dd08ebf6SMatthew Brost int err; 681dd08ebf6SMatthew Brost 682dd08ebf6SMatthew Brost /* make sure the status was cleared the last time we reset the uc */ 683c73acc1eSFrancois Dugast xe_assert(xe, !xe_uc_fw_is_loaded(uc_fw)); 684dd08ebf6SMatthew Brost 685dd08ebf6SMatthew Brost if (!xe_uc_fw_is_loadable(uc_fw)) 686dd08ebf6SMatthew Brost return -ENOEXEC; 687dd08ebf6SMatthew Brost 688dd08ebf6SMatthew Brost /* Call custom loader */ 689dd08ebf6SMatthew Brost err = uc_fw_xfer(uc_fw, offset, dma_flags); 690dd08ebf6SMatthew Brost if (err) 691dd08ebf6SMatthew Brost goto fail; 692dd08ebf6SMatthew Brost 693dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_TRANSFERRED); 694dd08ebf6SMatthew Brost return 0; 695dd08ebf6SMatthew Brost 696dd08ebf6SMatthew Brost fail: 697dd08ebf6SMatthew Brost drm_err(&xe->drm, "Failed to load %s firmware %s (%d)\n", 698dd08ebf6SMatthew Brost xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, 699dd08ebf6SMatthew Brost err); 700dd08ebf6SMatthew Brost xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_LOAD_FAIL); 701dd08ebf6SMatthew Brost return err; 702dd08ebf6SMatthew Brost } 703dd08ebf6SMatthew Brost 704*2e7227b4SDaniele Ceraolo Spurio static const char *version_type_repr(enum xe_uc_fw_version_types type) 705*2e7227b4SDaniele Ceraolo Spurio { 706*2e7227b4SDaniele Ceraolo Spurio switch (type) { 707*2e7227b4SDaniele Ceraolo Spurio case XE_UC_FW_VER_RELEASE: 708*2e7227b4SDaniele Ceraolo Spurio return "release"; 709*2e7227b4SDaniele Ceraolo Spurio case XE_UC_FW_VER_COMPATIBILITY: 710*2e7227b4SDaniele Ceraolo Spurio return "compatibility"; 711*2e7227b4SDaniele Ceraolo Spurio default: 712*2e7227b4SDaniele Ceraolo Spurio return "Unknown version type"; 713*2e7227b4SDaniele Ceraolo Spurio } 714*2e7227b4SDaniele Ceraolo Spurio } 715dd08ebf6SMatthew Brost 716dd08ebf6SMatthew Brost void xe_uc_fw_print(struct xe_uc_fw *uc_fw, struct drm_printer *p) 717dd08ebf6SMatthew Brost { 718*2e7227b4SDaniele Ceraolo Spurio int i; 719*2e7227b4SDaniele Ceraolo Spurio 720dd08ebf6SMatthew Brost drm_printf(p, "%s firmware: %s\n", 721dd08ebf6SMatthew Brost xe_uc_fw_type_repr(uc_fw->type), uc_fw->path); 722dd08ebf6SMatthew Brost drm_printf(p, "\tstatus: %s\n", 723dd08ebf6SMatthew Brost xe_uc_fw_status_repr(uc_fw->status)); 72499c821b0SMatthew Brost 725*2e7227b4SDaniele Ceraolo Spurio print_uc_fw_version(p, &uc_fw->versions.wanted, "\twanted %s", 726*2e7227b4SDaniele Ceraolo Spurio version_type_repr(uc_fw->versions.wanted_type)); 72799c821b0SMatthew Brost 728*2e7227b4SDaniele Ceraolo Spurio for (i = 0; i < XE_UC_FW_VER_TYPE_COUNT; i++) { 729*2e7227b4SDaniele Ceraolo Spurio struct xe_uc_fw_version *ver = &uc_fw->versions.found[i]; 730*2e7227b4SDaniele Ceraolo Spurio 731*2e7227b4SDaniele Ceraolo Spurio if (ver->major) 732*2e7227b4SDaniele Ceraolo Spurio print_uc_fw_version(p, ver, "\tfound %s", 733*2e7227b4SDaniele Ceraolo Spurio version_type_repr(i)); 73499c821b0SMatthew Brost } 735*2e7227b4SDaniele Ceraolo Spurio 736*2e7227b4SDaniele Ceraolo Spurio if (uc_fw->ucode_size) 737*2e7227b4SDaniele Ceraolo Spurio drm_printf(p, "\tuCode: %u bytes\n", uc_fw->ucode_size); 738*2e7227b4SDaniele Ceraolo Spurio if (uc_fw->rsa_size) 739*2e7227b4SDaniele Ceraolo Spurio drm_printf(p, "\tRSA: %u bytes\n", uc_fw->rsa_size); 740dd08ebf6SMatthew Brost } 741