xref: /linux/drivers/gpu/drm/xe/xe_uc_fw.c (revision de848da12f752170c2ebe114804a985314fd5a6a)
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"
15985d5a49SDaniele Ceraolo Spurio #include "xe_gsc.h"
16dd08ebf6SMatthew Brost #include "xe_gt.h"
1726a22952SMichal Wajdeczko #include "xe_gt_printk.h"
184eb0aab6SJulia Filipchuk #include "xe_guc.h"
19dd08ebf6SMatthew Brost #include "xe_map.h"
20dd08ebf6SMatthew Brost #include "xe_mmio.h"
21a455ed04SDaniele Ceraolo Spurio #include "xe_module.h"
2266cb3ca9SMichal Wajdeczko #include "xe_sriov.h"
23dd08ebf6SMatthew Brost #include "xe_uc_fw.h"
24dd08ebf6SMatthew Brost 
25ad55ead7SLucas De Marchi /*
26ad55ead7SLucas De Marchi  * List of required GuC and HuC binaries per-platform. They must be ordered
27ad55ead7SLucas De Marchi  * based on platform, from newer to older.
28ad55ead7SLucas De Marchi  *
29ad55ead7SLucas De Marchi  * Versioning follows the guidelines from
30ad55ead7SLucas De Marchi  * Documentation/driver-api/firmware/firmware-usage-guidelines.rst. There is a
31ad55ead7SLucas De Marchi  * distinction for platforms being officially supported by the driver or not.
32ad55ead7SLucas De Marchi  * Platforms not available publicly or not yet officially supported by the
33ad55ead7SLucas De Marchi  * driver (under force-probe), use the mmp_ver(): the firmware autoselect logic
34ad55ead7SLucas De Marchi  * will select the firmware from disk with filename that matches the full
35ad55ead7SLucas De Marchi  * "mpp version", i.e. major.minor.patch. mmp_ver() should only be used for
36ad55ead7SLucas De Marchi  * this case.
37ad55ead7SLucas De Marchi  *
38ad55ead7SLucas De Marchi  * For platforms officially supported by the driver, the filename always only
39ad55ead7SLucas De Marchi  * ever contains the major version (GuC) or no version at all (HuC).
40ad55ead7SLucas De Marchi  *
41ad55ead7SLucas De Marchi  * After loading the file, the driver parses the versions embedded in the blob.
42ad55ead7SLucas De Marchi  * The major version needs to match a major version supported by the driver (if
43ad55ead7SLucas De Marchi  * any). The minor version is also checked and a notice emitted to the log if
44ad55ead7SLucas De Marchi  * the version found is smaller than the version wanted. This is done only for
45ad55ead7SLucas De Marchi  * informational purposes so users may have a chance to upgrade, but the driver
46ad55ead7SLucas De Marchi  * still loads and use the older firmware.
47ad55ead7SLucas De Marchi  *
48ad55ead7SLucas De Marchi  * Examples:
49ad55ead7SLucas De Marchi  *
50ad55ead7SLucas De Marchi  *	1) Platform officially supported by i915 - using Tigerlake as example.
51ad55ead7SLucas De Marchi  *	   Driver loads the following firmware blobs from disk:
52ad55ead7SLucas De Marchi  *
53ad55ead7SLucas De Marchi  *		- i915/tgl_guc_<major>.bin
54ad55ead7SLucas De Marchi  *		- i915/tgl_huc.bin
55ad55ead7SLucas De Marchi  *
56ad55ead7SLucas De Marchi  *	   <major> number for GuC is checked that it matches the version inside
57ad55ead7SLucas De Marchi  *	   the blob. <minor> version is checked and if smaller than the expected
58ad55ead7SLucas De Marchi  *	   an info message is emitted about that.
59ad55ead7SLucas De Marchi  *
60ad55ead7SLucas De Marchi  *	1) XE_<FUTUREINTELPLATFORM>, still under require_force_probe. Using
61ad55ead7SLucas De Marchi  *	   "wipplat" as a short-name. Driver loads the following firmware blobs
62ad55ead7SLucas De Marchi  *	   from disk:
63ad55ead7SLucas De Marchi  *
64ad55ead7SLucas De Marchi  *		- xe/wipplat_guc_<major>.<minor>.<patch>.bin
65ad55ead7SLucas De Marchi  *		- xe/wipplat_huc_<major>.<minor>.<patch>.bin
66ad55ead7SLucas De Marchi  *
67ad55ead7SLucas De Marchi  *	   <major> and <minor> are checked that they match the version inside
68ad55ead7SLucas De Marchi  *	   the blob. Both of them need to match exactly what the driver is
69ad55ead7SLucas De Marchi  *	   expecting, otherwise it fails.
70ad55ead7SLucas De Marchi  *
71ad55ead7SLucas De Marchi  *	3) Platform officially supported by xe and out of force-probe. Using
72ad55ead7SLucas De Marchi  *	   "plat" as a short-name. Except for the different directory, the
73ad55ead7SLucas De Marchi  *	   behavior is the same as (1). Driver loads the following firmware
74ad55ead7SLucas De Marchi  *	   blobs from disk:
75ad55ead7SLucas De Marchi  *
76ad55ead7SLucas De Marchi  *		- xe/plat_guc_<major>.bin
77ad55ead7SLucas De Marchi  *		- xe/plat_huc.bin
78ad55ead7SLucas De Marchi  *
79ad55ead7SLucas De Marchi  *	   <major> number for GuC is checked that it matches the version inside
80ad55ead7SLucas De Marchi  *	   the blob. <minor> version is checked and if smaller than the expected
81ad55ead7SLucas De Marchi  *	   an info message is emitted about that.
82ad55ead7SLucas De Marchi  *
83ad55ead7SLucas De Marchi  * For the platforms already released with a major version, they should never be
84ad55ead7SLucas De Marchi  * removed from the table. Instead new entries with newer versions may be added
85ad55ead7SLucas De Marchi  * before them, so they take precedence.
86ad55ead7SLucas De Marchi  *
87ad55ead7SLucas De Marchi  * TODO: Currently there's no fallback on major version. That's because xe
88ad55ead7SLucas De Marchi  * driver only supports the one major version of each firmware in the table.
89ad55ead7SLucas De Marchi  * This needs to be fixed when the major version of GuC is updated.
90ad55ead7SLucas De Marchi  */
91ad55ead7SLucas De Marchi 
92ad55ead7SLucas De Marchi struct uc_fw_entry {
93ad55ead7SLucas De Marchi 	enum xe_platform platform;
94ad55ead7SLucas De Marchi 	struct {
95ad55ead7SLucas De Marchi 		const char *path;
96ad55ead7SLucas De Marchi 		u16 major;
97ad55ead7SLucas De Marchi 		u16 minor;
986650ad3eSJohn Harrison 		u16 patch;
99ad55ead7SLucas De Marchi 		bool full_ver_required;
100ad55ead7SLucas De Marchi 	};
101ad55ead7SLucas De Marchi };
102ad55ead7SLucas De Marchi 
103ad55ead7SLucas De Marchi struct fw_blobs_by_type {
104ad55ead7SLucas De Marchi 	const struct uc_fw_entry *entries;
105ad55ead7SLucas De Marchi 	u32 count;
106ad55ead7SLucas De Marchi };
107ad55ead7SLucas De Marchi 
108ad55ead7SLucas De Marchi #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver)			\
1099cc033e0SJulia Filipchuk 	fw_def(BATTLEMAGE,	major_ver(xe,	guc,	bmg,	70, 29, 2))	\
1104eb0aab6SJulia Filipchuk 	fw_def(LUNARLAKE,	major_ver(xe,	guc,	lnl,	70, 29, 2))	\
1114eb0aab6SJulia Filipchuk 	fw_def(METEORLAKE,	major_ver(i915,	guc,	mtl,	70, 29, 2))	\
1124eb0aab6SJulia Filipchuk 	fw_def(DG2,		major_ver(i915,	guc,	dg2,	70, 29, 2))	\
1134eb0aab6SJulia Filipchuk 	fw_def(DG1,		major_ver(i915,	guc,	dg1,	70, 29, 2))	\
1144eb0aab6SJulia Filipchuk 	fw_def(ALDERLAKE_N,	major_ver(i915,	guc,	tgl,	70, 29, 2))	\
1154eb0aab6SJulia Filipchuk 	fw_def(ALDERLAKE_P,	major_ver(i915,	guc,	adlp,	70, 29, 2))	\
1164eb0aab6SJulia Filipchuk 	fw_def(ALDERLAKE_S,	major_ver(i915,	guc,	tgl,	70, 29, 2))	\
1174eb0aab6SJulia Filipchuk 	fw_def(ROCKETLAKE,	major_ver(i915,	guc,	tgl,	70, 29, 2))	\
1184eb0aab6SJulia Filipchuk 	fw_def(TIGERLAKE,	major_ver(i915,	guc,	tgl,	70, 29, 2))
119ad55ead7SLucas De Marchi 
120ad55ead7SLucas De Marchi #define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver)		\
121351a8871SDaniele Ceraolo Spurio 	fw_def(BATTLEMAGE,	no_ver(xe,	huc,		bmg))		\
122e8149028SDaniele Ceraolo Spurio 	fw_def(LUNARLAKE,	no_ver(xe,	huc,		lnl))		\
123bfeb4ac5SDaniele Ceraolo Spurio 	fw_def(METEORLAKE,	no_ver(i915,	huc_gsc,	mtl))		\
124420c6a6fSDaniele Ceraolo Spurio 	fw_def(DG1,		no_ver(i915,	huc,		dg1))		\
12585635f5dSLucas De Marchi 	fw_def(ALDERLAKE_P,	no_ver(i915,	huc,		tgl))		\
126ad55ead7SLucas De Marchi 	fw_def(ALDERLAKE_S,	no_ver(i915,	huc,		tgl))		\
12794324e6bSAnusha Srivatsa 	fw_def(ROCKETLAKE,	no_ver(i915,	huc,		tgl))		\
128ad55ead7SLucas De Marchi 	fw_def(TIGERLAKE,	no_ver(i915,	huc,		tgl))
129ad55ead7SLucas De Marchi 
1305152234eSDaniele Ceraolo Spurio /* for the GSC FW we match the compatibility version and not the release one */
1315152234eSDaniele Ceraolo Spurio #define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver)		\
132*f7c2ea68SDaniele Ceraolo Spurio 	fw_def(LUNARLAKE,	major_ver(xe,	gsc,	lnl,	104, 1, 0)) \
133*f7c2ea68SDaniele Ceraolo Spurio 	fw_def(METEORLAKE,	major_ver(i915,	gsc,	mtl,	102, 1, 0))
1345152234eSDaniele Ceraolo Spurio 
135ad55ead7SLucas De Marchi #define MAKE_FW_PATH(dir__, uc__, shortname__, version__)			\
136ad55ead7SLucas De Marchi 	__stringify(dir__) "/" __stringify(shortname__) "_" __stringify(uc__) version__ ".bin"
137ad55ead7SLucas De Marchi 
138ad55ead7SLucas De Marchi #define fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c)			\
139ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a ## . ## b ## . ## c))
1406650ad3eSJohn Harrison #define fw_filename_major_ver(dir_, uc_, shortname_, a, b, c)			\
141ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(a))
142ad55ead7SLucas De Marchi #define fw_filename_no_ver(dir_, uc_, shortname_)				\
143ad55ead7SLucas De Marchi 	MAKE_FW_PATH(dir_, uc_, shortname_, "")
144*f7c2ea68SDaniele Ceraolo Spurio #define fw_filename_gsc(dir_, uc_, shortname_, a, b, c)				\
145*f7c2ea68SDaniele Ceraolo Spurio 	MAKE_FW_PATH(dir_, uc_, shortname_, "_" __stringify(b))
146ad55ead7SLucas De Marchi 
147ad55ead7SLucas De Marchi #define uc_fw_entry_mmp_ver(dir_, uc_, shortname_, a, b, c)			\
148ad55ead7SLucas De Marchi 	{ fw_filename_mmp_ver(dir_, uc_, shortname_, a, b, c),			\
1496650ad3eSJohn Harrison 	  a, b, c, true }
1506650ad3eSJohn Harrison #define uc_fw_entry_major_ver(dir_, uc_, shortname_, a, b, c)			\
1516650ad3eSJohn Harrison 	{ fw_filename_major_ver(dir_, uc_, shortname_, a, b, c),		\
1526650ad3eSJohn Harrison 	  a, b, c }
153ad55ead7SLucas De Marchi #define uc_fw_entry_no_ver(dir_, uc_, shortname_)				\
154ad55ead7SLucas De Marchi 	{ fw_filename_no_ver(dir_, uc_, shortname_),				\
155ad55ead7SLucas De Marchi 	  0, 0 }
156*f7c2ea68SDaniele Ceraolo Spurio #define uc_fw_entry_gsc(dir_, uc_, shortname_, a, b, c)				\
157*f7c2ea68SDaniele Ceraolo Spurio 	{ fw_filename_gsc(dir_, uc_, shortname_, a, b, c),			\
158*f7c2ea68SDaniele Ceraolo Spurio 	  a, b, c }
159ad55ead7SLucas De Marchi 
160ad55ead7SLucas De Marchi /* All blobs need to be declared via MODULE_FIRMWARE() */
161ad55ead7SLucas De Marchi #define XE_UC_MODULE_FIRMWARE(platform__, fw_filename)				\
162ad55ead7SLucas De Marchi 	MODULE_FIRMWARE(fw_filename);
163ad55ead7SLucas De Marchi 
164ad55ead7SLucas De Marchi #define XE_UC_FW_ENTRY(platform__, entry__)					\
165ad55ead7SLucas De Marchi 	{									\
166ad55ead7SLucas De Marchi 		.platform = XE_ ## platform__,					\
167ad55ead7SLucas De Marchi 		entry__,							\
168ad55ead7SLucas De Marchi 	},
169ad55ead7SLucas De Marchi 
1703e8e7ee6SFrancois Dugast XE_GUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE,
171ad55ead7SLucas De Marchi 		     fw_filename_mmp_ver, fw_filename_major_ver)
1723e8e7ee6SFrancois Dugast XE_HUC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE,
173ad55ead7SLucas De Marchi 		     fw_filename_mmp_ver, fw_filename_no_ver)
174*f7c2ea68SDaniele Ceraolo Spurio XE_GSC_FIRMWARE_DEFS(XE_UC_MODULE_FIRMWARE, fw_filename_gsc)
175ad55ead7SLucas De Marchi 
176dd08ebf6SMatthew Brost static struct xe_gt *
177dd08ebf6SMatthew Brost __uc_fw_to_gt(struct xe_uc_fw *uc_fw, enum xe_uc_fw_type type)
178dd08ebf6SMatthew Brost {
1790d1caff4SDaniele Ceraolo Spurio 	XE_WARN_ON(type >= XE_UC_FW_NUM_TYPES);
180dd08ebf6SMatthew Brost 
1810d1caff4SDaniele Ceraolo Spurio 	switch (type) {
1820d1caff4SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GUC:
1830d1caff4SDaniele Ceraolo Spurio 		return container_of(uc_fw, struct xe_gt, uc.guc.fw);
1840d1caff4SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
185dd08ebf6SMatthew Brost 		return container_of(uc_fw, struct xe_gt, uc.huc.fw);
1860d1caff4SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
1870d1caff4SDaniele Ceraolo Spurio 		return container_of(uc_fw, struct xe_gt, uc.gsc.fw);
1880d1caff4SDaniele Ceraolo Spurio 	default:
1890d1caff4SDaniele Ceraolo Spurio 		return NULL;
1900d1caff4SDaniele Ceraolo Spurio 	}
191dd08ebf6SMatthew Brost }
192dd08ebf6SMatthew Brost 
193dd08ebf6SMatthew Brost static struct xe_gt *uc_fw_to_gt(struct xe_uc_fw *uc_fw)
194dd08ebf6SMatthew Brost {
195dd08ebf6SMatthew Brost 	return __uc_fw_to_gt(uc_fw, uc_fw->type);
196dd08ebf6SMatthew Brost }
197dd08ebf6SMatthew Brost 
198dd08ebf6SMatthew Brost static struct xe_device *uc_fw_to_xe(struct xe_uc_fw *uc_fw)
199dd08ebf6SMatthew Brost {
200dd08ebf6SMatthew Brost 	return gt_to_xe(uc_fw_to_gt(uc_fw));
201dd08ebf6SMatthew Brost }
202dd08ebf6SMatthew Brost 
203dd08ebf6SMatthew Brost static void
204dd08ebf6SMatthew Brost uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw)
205dd08ebf6SMatthew Brost {
206ad55ead7SLucas De Marchi 	static const struct uc_fw_entry entries_guc[] = {
207ad55ead7SLucas De Marchi 		XE_GUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
208ad55ead7SLucas De Marchi 				     uc_fw_entry_mmp_ver,
209ad55ead7SLucas De Marchi 				     uc_fw_entry_major_ver)
210dd08ebf6SMatthew Brost 	};
211ad55ead7SLucas De Marchi 	static const struct uc_fw_entry entries_huc[] = {
212ad55ead7SLucas De Marchi 		XE_HUC_FIRMWARE_DEFS(XE_UC_FW_ENTRY,
213ad55ead7SLucas De Marchi 				     uc_fw_entry_mmp_ver,
214ad55ead7SLucas De Marchi 				     uc_fw_entry_no_ver)
215dd08ebf6SMatthew Brost 	};
2165152234eSDaniele Ceraolo Spurio 	static const struct uc_fw_entry entries_gsc[] = {
217*f7c2ea68SDaniele Ceraolo Spurio 		XE_GSC_FIRMWARE_DEFS(XE_UC_FW_ENTRY, uc_fw_entry_gsc)
2185152234eSDaniele Ceraolo Spurio 	};
219dd08ebf6SMatthew Brost 	static const struct fw_blobs_by_type blobs_all[XE_UC_FW_NUM_TYPES] = {
220ad55ead7SLucas De Marchi 		[XE_UC_FW_TYPE_GUC] = { entries_guc, ARRAY_SIZE(entries_guc) },
221ad55ead7SLucas De Marchi 		[XE_UC_FW_TYPE_HUC] = { entries_huc, ARRAY_SIZE(entries_huc) },
2225152234eSDaniele Ceraolo Spurio 		[XE_UC_FW_TYPE_GSC] = { entries_gsc, ARRAY_SIZE(entries_gsc) },
223dd08ebf6SMatthew Brost 	};
224ad55ead7SLucas De Marchi 	static const struct uc_fw_entry *entries;
225dd08ebf6SMatthew Brost 	enum xe_platform p = xe->info.platform;
226ad55ead7SLucas De Marchi 	u32 count;
227dd08ebf6SMatthew Brost 	int i;
228dd08ebf6SMatthew Brost 
229c73acc1eSFrancois Dugast 	xe_assert(xe, uc_fw->type < ARRAY_SIZE(blobs_all));
230ad55ead7SLucas De Marchi 	entries = blobs_all[uc_fw->type].entries;
231ad55ead7SLucas De Marchi 	count = blobs_all[uc_fw->type].count;
232dd08ebf6SMatthew Brost 
233ad55ead7SLucas De Marchi 	for (i = 0; i < count && p <= entries[i].platform; i++) {
234ad55ead7SLucas De Marchi 		if (p == entries[i].platform) {
235ad55ead7SLucas De Marchi 			uc_fw->path = entries[i].path;
2362e7227b4SDaniele Ceraolo Spurio 			uc_fw->versions.wanted.major = entries[i].major;
2372e7227b4SDaniele Ceraolo Spurio 			uc_fw->versions.wanted.minor = entries[i].minor;
2386650ad3eSJohn Harrison 			uc_fw->versions.wanted.patch = entries[i].patch;
239ad55ead7SLucas De Marchi 			uc_fw->full_ver_required = entries[i].full_ver_required;
2402e7227b4SDaniele Ceraolo Spurio 
2410881cbe0SDaniele Ceraolo Spurio 			if (uc_fw->type == XE_UC_FW_TYPE_GSC)
2420881cbe0SDaniele Ceraolo Spurio 				uc_fw->versions.wanted_type = XE_UC_FW_VER_COMPATIBILITY;
2430881cbe0SDaniele Ceraolo Spurio 			else
2442e7227b4SDaniele Ceraolo Spurio 				uc_fw->versions.wanted_type = XE_UC_FW_VER_RELEASE;
2450881cbe0SDaniele Ceraolo Spurio 
246dd08ebf6SMatthew Brost 			break;
247dd08ebf6SMatthew Brost 		}
248dd08ebf6SMatthew Brost 	}
249dd08ebf6SMatthew Brost }
250dd08ebf6SMatthew Brost 
251a455ed04SDaniele Ceraolo Spurio static void
252a455ed04SDaniele Ceraolo Spurio uc_fw_override(struct xe_uc_fw *uc_fw)
253a455ed04SDaniele Ceraolo Spurio {
254a455ed04SDaniele Ceraolo Spurio 	char *path_override = NULL;
255a455ed04SDaniele Ceraolo Spurio 
256a455ed04SDaniele Ceraolo Spurio 	/* empty string disables, but it's not allowed for GuC */
257a455ed04SDaniele Ceraolo Spurio 	switch (uc_fw->type) {
258a455ed04SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GUC:
259adce1b39SBommithi Sakeena 		if (xe_modparam.guc_firmware_path && *xe_modparam.guc_firmware_path)
260adce1b39SBommithi Sakeena 			path_override = xe_modparam.guc_firmware_path;
261a455ed04SDaniele Ceraolo Spurio 		break;
262a455ed04SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
263adce1b39SBommithi Sakeena 		path_override = xe_modparam.huc_firmware_path;
264a455ed04SDaniele Ceraolo Spurio 		break;
2655152234eSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
2665152234eSDaniele Ceraolo Spurio 		path_override = xe_modparam.gsc_firmware_path;
2675152234eSDaniele Ceraolo Spurio 		break;
268a455ed04SDaniele Ceraolo Spurio 	default:
269a455ed04SDaniele Ceraolo Spurio 		break;
270a455ed04SDaniele Ceraolo Spurio 	}
271a455ed04SDaniele Ceraolo Spurio 
272a455ed04SDaniele Ceraolo Spurio 	if (path_override) {
273a455ed04SDaniele Ceraolo Spurio 		uc_fw->path = path_override;
274a455ed04SDaniele Ceraolo Spurio 		uc_fw->user_overridden = true;
275a455ed04SDaniele Ceraolo Spurio 	}
276a455ed04SDaniele Ceraolo Spurio }
277a455ed04SDaniele Ceraolo Spurio 
278dd08ebf6SMatthew Brost /**
279dd08ebf6SMatthew Brost  * xe_uc_fw_copy_rsa - copy fw RSA to buffer
280dd08ebf6SMatthew Brost  *
281dd08ebf6SMatthew Brost  * @uc_fw: uC firmware
282dd08ebf6SMatthew Brost  * @dst: dst buffer
283dd08ebf6SMatthew Brost  * @max_len: max number of bytes to copy
284dd08ebf6SMatthew Brost  *
285dd08ebf6SMatthew Brost  * Return: number of copied bytes.
286dd08ebf6SMatthew Brost  */
287dd08ebf6SMatthew Brost size_t xe_uc_fw_copy_rsa(struct xe_uc_fw *uc_fw, void *dst, u32 max_len)
288dd08ebf6SMatthew Brost {
289dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
290dd08ebf6SMatthew Brost 	u32 size = min_t(u32, uc_fw->rsa_size, max_len);
291dd08ebf6SMatthew Brost 
292c73acc1eSFrancois Dugast 	xe_assert(xe, !(size % 4));
293c73acc1eSFrancois Dugast 	xe_assert(xe, xe_uc_fw_is_available(uc_fw));
294dd08ebf6SMatthew Brost 
295dd08ebf6SMatthew Brost 	xe_map_memcpy_from(xe, dst, &uc_fw->bo->vmap,
296dd08ebf6SMatthew Brost 			   xe_uc_fw_rsa_offset(uc_fw), size);
297dd08ebf6SMatthew Brost 
298dd08ebf6SMatthew Brost 	return size;
299dd08ebf6SMatthew Brost }
300dd08ebf6SMatthew Brost 
301dd08ebf6SMatthew Brost static void uc_fw_fini(struct drm_device *drm, void *arg)
302dd08ebf6SMatthew Brost {
303dd08ebf6SMatthew Brost 	struct xe_uc_fw *uc_fw = arg;
304dd08ebf6SMatthew Brost 
305dd08ebf6SMatthew Brost 	if (!xe_uc_fw_is_available(uc_fw))
306dd08ebf6SMatthew Brost 		return;
307dd08ebf6SMatthew Brost 
308dd08ebf6SMatthew Brost 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_SELECTED);
309dd08ebf6SMatthew Brost }
310dd08ebf6SMatthew Brost 
31143c4ff3cSDaniele Ceraolo Spurio static int guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css)
31299c821b0SMatthew Brost {
31399c821b0SMatthew Brost 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
3142e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
3152e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *compatibility = &uc_fw->versions.found[XE_UC_FW_VER_COMPATIBILITY];
31699c821b0SMatthew Brost 
317c73acc1eSFrancois Dugast 	xe_gt_assert(gt, uc_fw->type == XE_UC_FW_TYPE_GUC);
31899c821b0SMatthew Brost 
3194eb0aab6SJulia Filipchuk 	/* We don't support GuC releases older than 70.29.2 */
3204eb0aab6SJulia Filipchuk 	if (MAKE_GUC_VER_STRUCT(*release) < MAKE_GUC_VER(70, 29, 2)) {
3214eb0aab6SJulia Filipchuk 		xe_gt_err(gt, "Unsupported GuC v%u.%u.%u! v70.29.2 or newer is required\n",
3224eb0aab6SJulia Filipchuk 			  release->major, release->minor, release->patch);
32343c4ff3cSDaniele Ceraolo Spurio 		return -EINVAL;
32499c821b0SMatthew Brost 	}
32599c821b0SMatthew Brost 
32643c4ff3cSDaniele Ceraolo Spurio 	compatibility->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css->submission_version);
32743c4ff3cSDaniele Ceraolo Spurio 	compatibility->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css->submission_version);
32843c4ff3cSDaniele Ceraolo Spurio 	compatibility->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css->submission_version);
32943c4ff3cSDaniele Ceraolo Spurio 
33099c821b0SMatthew Brost 	uc_fw->private_data_size = css->private_data_size;
33143c4ff3cSDaniele Ceraolo Spurio 
33243c4ff3cSDaniele Ceraolo Spurio 	return 0;
33399c821b0SMatthew Brost }
33499c821b0SMatthew Brost 
3350881cbe0SDaniele Ceraolo Spurio int xe_uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw)
336ad55ead7SLucas De Marchi {
337ad55ead7SLucas De Marchi 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
3382e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted;
3392e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *found = &uc_fw->versions.found[uc_fw->versions.wanted_type];
340ad55ead7SLucas De Marchi 
341ad55ead7SLucas De Marchi 	/* Driver has no requirement on any version, any is good. */
3422e7227b4SDaniele Ceraolo Spurio 	if (!wanted->major)
343ad55ead7SLucas De Marchi 		return 0;
344ad55ead7SLucas De Marchi 
345ad55ead7SLucas De Marchi 	/*
346ad55ead7SLucas De Marchi 	 * If full version is required, both major and minor should match.
347ad55ead7SLucas De Marchi 	 * Otherwise, at least the major version.
348ad55ead7SLucas De Marchi 	 */
3492e7227b4SDaniele Ceraolo Spurio 	if (wanted->major != found->major ||
3506650ad3eSJohn Harrison 	    (uc_fw->full_ver_required &&
3516650ad3eSJohn Harrison 	     ((wanted->minor != found->minor) ||
3526650ad3eSJohn Harrison 	      (wanted->patch != found->patch)))) {
3536650ad3eSJohn Harrison 		drm_notice(&xe->drm, "%s firmware %s: unexpected version: %u.%u.%u != %u.%u.%u\n",
354ad55ead7SLucas De Marchi 			   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
3556650ad3eSJohn Harrison 			   found->major, found->minor, found->patch,
3566650ad3eSJohn Harrison 			   wanted->major, wanted->minor, wanted->patch);
357ad55ead7SLucas De Marchi 		goto fail;
358ad55ead7SLucas De Marchi 	}
359ad55ead7SLucas De Marchi 
3606650ad3eSJohn Harrison 	if (wanted->minor > found->minor ||
3616650ad3eSJohn Harrison 	    (wanted->minor == found->minor && wanted->patch > found->patch)) {
3626650ad3eSJohn Harrison 		drm_notice(&xe->drm, "%s firmware (%u.%u.%u) is recommended, but only (%u.%u.%u) was found in %s\n",
363ad55ead7SLucas De Marchi 			   xe_uc_fw_type_repr(uc_fw->type),
3646650ad3eSJohn Harrison 			   wanted->major, wanted->minor, wanted->patch,
3656650ad3eSJohn Harrison 			   found->major, found->minor, found->patch,
366ad55ead7SLucas De Marchi 			   uc_fw->path);
367ad55ead7SLucas De Marchi 		drm_info(&xe->drm, "Consider updating your linux-firmware pkg or downloading from %s\n",
368ad55ead7SLucas De Marchi 			 XE_UC_FIRMWARE_URL);
369ad55ead7SLucas De Marchi 	}
370ad55ead7SLucas De Marchi 
371ad55ead7SLucas De Marchi 	return 0;
372ad55ead7SLucas De Marchi 
373ad55ead7SLucas De Marchi fail:
374ad55ead7SLucas De Marchi 	if (xe_uc_fw_is_overridden(uc_fw))
375ad55ead7SLucas De Marchi 		return 0;
376ad55ead7SLucas De Marchi 
377ad55ead7SLucas De Marchi 	return -ENOEXEC;
378ad55ead7SLucas De Marchi }
379ad55ead7SLucas De Marchi 
380a9a95523SDaniele Ceraolo Spurio /* Refer to the "CSS-based Firmware Layout" documentation entry for details */
381a9a95523SDaniele Ceraolo Spurio static int parse_css_header(struct xe_uc_fw *uc_fw, const void *fw_data, size_t fw_size)
382a9a95523SDaniele Ceraolo Spurio {
383a9a95523SDaniele Ceraolo Spurio 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
3842e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
385a9a95523SDaniele Ceraolo Spurio 	struct uc_css_header *css;
386a9a95523SDaniele Ceraolo Spurio 	size_t size;
387a9a95523SDaniele Ceraolo Spurio 
388a9a95523SDaniele Ceraolo Spurio 	/* Check the size of the blob before examining buffer contents */
389a9a95523SDaniele Ceraolo Spurio 	if (unlikely(fw_size < sizeof(struct uc_css_header))) {
390a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n",
391a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
392a9a95523SDaniele Ceraolo Spurio 			 fw_size, sizeof(struct uc_css_header));
393a9a95523SDaniele Ceraolo Spurio 		return -ENODATA;
394a9a95523SDaniele Ceraolo Spurio 	}
395a9a95523SDaniele Ceraolo Spurio 
396a9a95523SDaniele Ceraolo Spurio 	css = (struct uc_css_header *)fw_data;
397a9a95523SDaniele Ceraolo Spurio 
398a9a95523SDaniele Ceraolo Spurio 	/* Check integrity of size values inside CSS header */
399a9a95523SDaniele Ceraolo Spurio 	size = (css->header_size_dw - css->key_size_dw - css->modulus_size_dw -
400a9a95523SDaniele Ceraolo Spurio 		css->exponent_size_dw) * sizeof(u32);
401a9a95523SDaniele Ceraolo Spurio 	if (unlikely(size != sizeof(struct uc_css_header))) {
402a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm,
403a9a95523SDaniele Ceraolo Spurio 			 "%s firmware %s: unexpected header size: %zu != %zu\n",
404a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
405a9a95523SDaniele Ceraolo Spurio 			 fw_size, sizeof(struct uc_css_header));
406a9a95523SDaniele Ceraolo Spurio 		return -EPROTO;
407a9a95523SDaniele Ceraolo Spurio 	}
408a9a95523SDaniele Ceraolo Spurio 
409a9a95523SDaniele Ceraolo Spurio 	/* uCode size must calculated from other sizes */
410a9a95523SDaniele Ceraolo Spurio 	uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
411a9a95523SDaniele Ceraolo Spurio 
412a9a95523SDaniele Ceraolo Spurio 	/* now RSA */
413a9a95523SDaniele Ceraolo Spurio 	uc_fw->rsa_size = css->key_size_dw * sizeof(u32);
414a9a95523SDaniele Ceraolo Spurio 
415a9a95523SDaniele Ceraolo Spurio 	/* At least, it should have header, uCode and RSA. Size of all three. */
416a9a95523SDaniele Ceraolo Spurio 	size = sizeof(struct uc_css_header) + uc_fw->ucode_size +
417a9a95523SDaniele Ceraolo Spurio 		uc_fw->rsa_size;
418a9a95523SDaniele Ceraolo Spurio 	if (unlikely(fw_size < size)) {
419a9a95523SDaniele Ceraolo Spurio 		drm_warn(&xe->drm, "%s firmware %s: invalid size: %zu < %zu\n",
420a9a95523SDaniele Ceraolo Spurio 			 xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
421a9a95523SDaniele Ceraolo Spurio 			 fw_size, size);
422a9a95523SDaniele Ceraolo Spurio 		return -ENOEXEC;
423a9a95523SDaniele Ceraolo Spurio 	}
424a9a95523SDaniele Ceraolo Spurio 
425a9a95523SDaniele Ceraolo Spurio 	/* Get version numbers from the CSS header */
4262e7227b4SDaniele Ceraolo Spurio 	release->major = FIELD_GET(CSS_SW_VERSION_UC_MAJOR, css->sw_version);
4272e7227b4SDaniele Ceraolo Spurio 	release->minor = FIELD_GET(CSS_SW_VERSION_UC_MINOR, css->sw_version);
4282e7227b4SDaniele Ceraolo Spurio 	release->patch = FIELD_GET(CSS_SW_VERSION_UC_PATCH, css->sw_version);
429a9a95523SDaniele Ceraolo Spurio 
430a9a95523SDaniele Ceraolo Spurio 	if (uc_fw->type == XE_UC_FW_TYPE_GUC)
43143c4ff3cSDaniele Ceraolo Spurio 		return guc_read_css_info(uc_fw, css);
432a9a95523SDaniele Ceraolo Spurio 
433a9a95523SDaniele Ceraolo Spurio 	return 0;
434a9a95523SDaniele Ceraolo Spurio }
435a9a95523SDaniele Ceraolo Spurio 
436484ecffaSDaniele Ceraolo Spurio static bool is_cpd_header(const void *data)
437484ecffaSDaniele Ceraolo Spurio {
438484ecffaSDaniele Ceraolo Spurio 	const u32 *marker = data;
439484ecffaSDaniele Ceraolo Spurio 
440484ecffaSDaniele Ceraolo Spurio 	return *marker == GSC_CPD_HEADER_MARKER;
441484ecffaSDaniele Ceraolo Spurio }
442484ecffaSDaniele Ceraolo Spurio 
443484ecffaSDaniele Ceraolo Spurio static u32 entry_offset(const struct gsc_cpd_header_v2 *header, const char *name)
444484ecffaSDaniele Ceraolo Spurio {
445484ecffaSDaniele Ceraolo Spurio 	const struct gsc_cpd_entry *entry;
446484ecffaSDaniele Ceraolo Spurio 	int i;
447484ecffaSDaniele Ceraolo Spurio 
448484ecffaSDaniele Ceraolo Spurio 	entry = (void *)header + header->header_length;
449484ecffaSDaniele Ceraolo Spurio 
450484ecffaSDaniele Ceraolo Spurio 	for (i = 0; i < header->num_of_entries; i++, entry++)
451484ecffaSDaniele Ceraolo Spurio 		if (strcmp(entry->name, name) == 0)
452484ecffaSDaniele Ceraolo Spurio 			return entry->offset & GSC_CPD_ENTRY_OFFSET_MASK;
453484ecffaSDaniele Ceraolo Spurio 
454484ecffaSDaniele Ceraolo Spurio 	return 0;
455484ecffaSDaniele Ceraolo Spurio }
456484ecffaSDaniele Ceraolo Spurio 
457484ecffaSDaniele Ceraolo Spurio /* Refer to the "GSC-based Firmware Layout" documentation entry for details */
458484ecffaSDaniele Ceraolo Spurio static int parse_cpd_header(struct xe_uc_fw *uc_fw, const void *data, size_t size,
459484ecffaSDaniele Ceraolo Spurio 			    const char *manifest_entry, const char *css_entry)
460484ecffaSDaniele Ceraolo Spurio {
461484ecffaSDaniele Ceraolo Spurio 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
462484ecffaSDaniele Ceraolo Spurio 	struct xe_device *xe = gt_to_xe(gt);
463484ecffaSDaniele Ceraolo Spurio 	const struct gsc_cpd_header_v2 *header = data;
4642e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *release = &uc_fw->versions.found[XE_UC_FW_VER_RELEASE];
465484ecffaSDaniele Ceraolo Spurio 	const struct gsc_manifest_header *manifest;
466484ecffaSDaniele Ceraolo Spurio 	size_t min_size = sizeof(*header);
467484ecffaSDaniele Ceraolo Spurio 	u32 offset;
468484ecffaSDaniele Ceraolo Spurio 
469484ecffaSDaniele Ceraolo Spurio 	/* manifest_entry is mandatory, css_entry is optional */
470484ecffaSDaniele Ceraolo Spurio 	xe_assert(xe, manifest_entry);
471484ecffaSDaniele Ceraolo Spurio 
472484ecffaSDaniele Ceraolo Spurio 	if (size < min_size || !is_cpd_header(header))
473484ecffaSDaniele Ceraolo Spurio 		return -ENOENT;
474484ecffaSDaniele Ceraolo Spurio 
475484ecffaSDaniele Ceraolo Spurio 	if (header->header_length < sizeof(struct gsc_cpd_header_v2)) {
476484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "invalid CPD header length %u!\n", header->header_length);
477484ecffaSDaniele Ceraolo Spurio 		return -EINVAL;
478484ecffaSDaniele Ceraolo Spurio 	}
479484ecffaSDaniele Ceraolo Spurio 
480484ecffaSDaniele Ceraolo Spurio 	min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries;
481484ecffaSDaniele Ceraolo Spurio 	if (size < min_size) {
482484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size);
483484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
484484ecffaSDaniele Ceraolo Spurio 	}
485484ecffaSDaniele Ceraolo Spurio 
486484ecffaSDaniele Ceraolo Spurio 	/* Look for the manifest first */
487484ecffaSDaniele Ceraolo Spurio 	offset = entry_offset(header, manifest_entry);
488484ecffaSDaniele Ceraolo Spurio 	if (!offset) {
489484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "Failed to find %s manifest!\n",
490484ecffaSDaniele Ceraolo Spurio 			  xe_uc_fw_type_repr(uc_fw->type));
491484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
492484ecffaSDaniele Ceraolo Spurio 	}
493484ecffaSDaniele Ceraolo Spurio 
494484ecffaSDaniele Ceraolo Spurio 	min_size = offset + sizeof(struct gsc_manifest_header);
495484ecffaSDaniele Ceraolo Spurio 	if (size < min_size) {
496484ecffaSDaniele Ceraolo Spurio 		xe_gt_err(gt, "FW too small! %zu < %zu\n", size, min_size);
497484ecffaSDaniele Ceraolo Spurio 		return -ENODATA;
498484ecffaSDaniele Ceraolo Spurio 	}
499484ecffaSDaniele Ceraolo Spurio 
500484ecffaSDaniele Ceraolo Spurio 	manifest = data + offset;
501484ecffaSDaniele Ceraolo Spurio 
5022e7227b4SDaniele Ceraolo Spurio 	release->major = manifest->fw_version.major;
5032e7227b4SDaniele Ceraolo Spurio 	release->minor = manifest->fw_version.minor;
5042e7227b4SDaniele Ceraolo Spurio 	release->patch = manifest->fw_version.hotfix;
505484ecffaSDaniele Ceraolo Spurio 
506985d5a49SDaniele Ceraolo Spurio 	if (uc_fw->type == XE_UC_FW_TYPE_GSC) {
507985d5a49SDaniele Ceraolo Spurio 		struct xe_gsc *gsc = container_of(uc_fw, struct xe_gsc, fw);
508985d5a49SDaniele Ceraolo Spurio 
509985d5a49SDaniele Ceraolo Spurio 		release->build = manifest->fw_version.build;
510985d5a49SDaniele Ceraolo Spurio 		gsc->security_version = manifest->security_version;
511985d5a49SDaniele Ceraolo Spurio 	}
512985d5a49SDaniele Ceraolo Spurio 
513484ecffaSDaniele Ceraolo Spurio 	/* then optionally look for the css header */
514484ecffaSDaniele Ceraolo Spurio 	if (css_entry) {
515484ecffaSDaniele Ceraolo Spurio 		int ret;
516484ecffaSDaniele Ceraolo Spurio 
517484ecffaSDaniele Ceraolo Spurio 		/*
518484ecffaSDaniele Ceraolo Spurio 		 * This section does not contain a CSS entry on DG2. We
519484ecffaSDaniele Ceraolo Spurio 		 * don't support DG2 HuC right now, so no need to handle
520484ecffaSDaniele Ceraolo Spurio 		 * it, just add a reminder in case that changes.
521484ecffaSDaniele Ceraolo Spurio 		 */
522484ecffaSDaniele Ceraolo Spurio 		xe_assert(xe, xe->info.platform != XE_DG2);
523484ecffaSDaniele Ceraolo Spurio 
524484ecffaSDaniele Ceraolo Spurio 		offset = entry_offset(header, css_entry);
525484ecffaSDaniele Ceraolo Spurio 
526484ecffaSDaniele Ceraolo Spurio 		/* the CSS header parser will check that the CSS header fits */
527484ecffaSDaniele Ceraolo Spurio 		if (offset > size) {
528484ecffaSDaniele Ceraolo Spurio 			xe_gt_err(gt, "FW too small! %zu < %u\n", size, offset);
529484ecffaSDaniele Ceraolo Spurio 			return -ENODATA;
530484ecffaSDaniele Ceraolo Spurio 		}
531484ecffaSDaniele Ceraolo Spurio 
532484ecffaSDaniele Ceraolo Spurio 		ret = parse_css_header(uc_fw, data + offset, size - offset);
533484ecffaSDaniele Ceraolo Spurio 		if (ret)
534484ecffaSDaniele Ceraolo Spurio 			return ret;
535484ecffaSDaniele Ceraolo Spurio 
536484ecffaSDaniele Ceraolo Spurio 		uc_fw->css_offset = offset;
537484ecffaSDaniele Ceraolo Spurio 	}
538484ecffaSDaniele Ceraolo Spurio 
539d8b15713SDaniele Ceraolo Spurio 	uc_fw->has_gsc_headers = true;
540d8b15713SDaniele Ceraolo Spurio 
541484ecffaSDaniele Ceraolo Spurio 	return 0;
542484ecffaSDaniele Ceraolo Spurio }
543484ecffaSDaniele Ceraolo Spurio 
544985d5a49SDaniele Ceraolo Spurio static int parse_gsc_layout(struct xe_uc_fw *uc_fw, const void *data, size_t size)
545985d5a49SDaniele Ceraolo Spurio {
546985d5a49SDaniele Ceraolo Spurio 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
547985d5a49SDaniele Ceraolo Spurio 	const struct gsc_layout_pointers *layout = data;
548985d5a49SDaniele Ceraolo Spurio 	const struct gsc_bpdt_header *bpdt_header = NULL;
549985d5a49SDaniele Ceraolo Spurio 	const struct gsc_bpdt_entry *bpdt_entry = NULL;
550985d5a49SDaniele Ceraolo Spurio 	size_t min_size = sizeof(*layout);
551985d5a49SDaniele Ceraolo Spurio 	int i;
552985d5a49SDaniele Ceraolo Spurio 
553985d5a49SDaniele Ceraolo Spurio 	if (size < min_size) {
554985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW too small! %zu < %zu\n", size, min_size);
555985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
556985d5a49SDaniele Ceraolo Spurio 	}
557985d5a49SDaniele Ceraolo Spurio 
558985d5a49SDaniele Ceraolo Spurio 	min_size = layout->boot1.offset + layout->boot1.size;
559985d5a49SDaniele Ceraolo Spurio 	if (size < min_size) {
560985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW too small for boot section! %zu < %zu\n",
561985d5a49SDaniele Ceraolo Spurio 			  size, min_size);
562985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
563985d5a49SDaniele Ceraolo Spurio 	}
564985d5a49SDaniele Ceraolo Spurio 
565985d5a49SDaniele Ceraolo Spurio 	min_size = sizeof(*bpdt_header);
566985d5a49SDaniele Ceraolo Spurio 	if (layout->boot1.size < min_size) {
567985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW boot section too small for BPDT header: %u < %zu\n",
568985d5a49SDaniele Ceraolo Spurio 			  layout->boot1.size, min_size);
569985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
570985d5a49SDaniele Ceraolo Spurio 	}
571985d5a49SDaniele Ceraolo Spurio 
572985d5a49SDaniele Ceraolo Spurio 	bpdt_header = data + layout->boot1.offset;
573985d5a49SDaniele Ceraolo Spurio 	if (bpdt_header->signature != GSC_BPDT_HEADER_SIGNATURE) {
574985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "invalid signature for BPDT header: 0x%08x!\n",
575985d5a49SDaniele Ceraolo Spurio 			  bpdt_header->signature);
576985d5a49SDaniele Ceraolo Spurio 		return -EINVAL;
577985d5a49SDaniele Ceraolo Spurio 	}
578985d5a49SDaniele Ceraolo Spurio 
579985d5a49SDaniele Ceraolo Spurio 	min_size += sizeof(*bpdt_entry) * bpdt_header->descriptor_count;
580985d5a49SDaniele Ceraolo Spurio 	if (layout->boot1.size < min_size) {
581985d5a49SDaniele Ceraolo Spurio 		xe_gt_err(gt, "GSC FW boot section too small for BPDT entries: %u < %zu\n",
582985d5a49SDaniele Ceraolo Spurio 			  layout->boot1.size, min_size);
583985d5a49SDaniele Ceraolo Spurio 		return -ENODATA;
584985d5a49SDaniele Ceraolo Spurio 	}
585985d5a49SDaniele Ceraolo Spurio 
586985d5a49SDaniele Ceraolo Spurio 	bpdt_entry = (void *)bpdt_header + sizeof(*bpdt_header);
587985d5a49SDaniele Ceraolo Spurio 	for (i = 0; i < bpdt_header->descriptor_count; i++, bpdt_entry++) {
588985d5a49SDaniele Ceraolo Spurio 		if ((bpdt_entry->type & GSC_BPDT_ENTRY_TYPE_MASK) !=
589985d5a49SDaniele Ceraolo Spurio 		    GSC_BPDT_ENTRY_TYPE_GSC_RBE)
590985d5a49SDaniele Ceraolo Spurio 			continue;
591985d5a49SDaniele Ceraolo Spurio 
592985d5a49SDaniele Ceraolo Spurio 		min_size = bpdt_entry->sub_partition_offset;
593985d5a49SDaniele Ceraolo Spurio 
594985d5a49SDaniele Ceraolo Spurio 		/* the CPD header parser will check that the CPD header fits */
595985d5a49SDaniele Ceraolo Spurio 		if (layout->boot1.size < min_size) {
596985d5a49SDaniele Ceraolo Spurio 			xe_gt_err(gt, "GSC FW boot section too small for CPD offset: %u < %zu\n",
597985d5a49SDaniele Ceraolo Spurio 				  layout->boot1.size, min_size);
598985d5a49SDaniele Ceraolo Spurio 			return -ENODATA;
599985d5a49SDaniele Ceraolo Spurio 		}
600985d5a49SDaniele Ceraolo Spurio 
601985d5a49SDaniele Ceraolo Spurio 		return parse_cpd_header(uc_fw,
602985d5a49SDaniele Ceraolo Spurio 					(void *)bpdt_header + min_size,
603985d5a49SDaniele Ceraolo Spurio 					layout->boot1.size - min_size,
604985d5a49SDaniele Ceraolo Spurio 					"RBEP.man", NULL);
605985d5a49SDaniele Ceraolo Spurio 	}
606985d5a49SDaniele Ceraolo Spurio 
607985d5a49SDaniele Ceraolo Spurio 	xe_gt_err(gt, "couldn't find CPD header in GSC binary!\n");
608985d5a49SDaniele Ceraolo Spurio 	return -ENODATA;
609985d5a49SDaniele Ceraolo Spurio }
610985d5a49SDaniele Ceraolo Spurio 
611a9a95523SDaniele Ceraolo Spurio static int parse_headers(struct xe_uc_fw *uc_fw, const struct firmware *fw)
612a9a95523SDaniele Ceraolo Spurio {
613484ecffaSDaniele Ceraolo Spurio 	int ret;
614484ecffaSDaniele Ceraolo Spurio 
615484ecffaSDaniele Ceraolo Spurio 	/*
616484ecffaSDaniele Ceraolo Spurio 	 * All GuC releases and older HuC ones use CSS headers, while newer HuC
617484ecffaSDaniele Ceraolo Spurio 	 * releases use GSC CPD headers.
618484ecffaSDaniele Ceraolo Spurio 	 */
619484ecffaSDaniele Ceraolo Spurio 	switch (uc_fw->type) {
620985d5a49SDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GSC:
621985d5a49SDaniele Ceraolo Spurio 		return parse_gsc_layout(uc_fw, fw->data, fw->size);
622484ecffaSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_HUC:
623484ecffaSDaniele Ceraolo Spurio 		ret = parse_cpd_header(uc_fw, fw->data, fw->size, "HUCP.man", "huc_fw");
624484ecffaSDaniele Ceraolo Spurio 		if (!ret || ret != -ENOENT)
625484ecffaSDaniele Ceraolo Spurio 			return ret;
626484ecffaSDaniele Ceraolo Spurio 		fallthrough;
627484ecffaSDaniele Ceraolo Spurio 	case XE_UC_FW_TYPE_GUC:
628a9a95523SDaniele Ceraolo Spurio 		return parse_css_header(uc_fw, fw->data, fw->size);
629484ecffaSDaniele Ceraolo Spurio 	default:
630484ecffaSDaniele Ceraolo Spurio 		return -EINVAL;
631484ecffaSDaniele Ceraolo Spurio 	}
632484ecffaSDaniele Ceraolo Spurio 
633484ecffaSDaniele Ceraolo Spurio 	return 0;
634a9a95523SDaniele Ceraolo Spurio }
635a9a95523SDaniele Ceraolo Spurio 
6362e7227b4SDaniele Ceraolo Spurio #define print_uc_fw_version(p_, version_, prefix_, ...) \
6372e7227b4SDaniele Ceraolo Spurio do { \
6382e7227b4SDaniele Ceraolo Spurio 	struct xe_uc_fw_version *ver_ = (version_); \
6392e7227b4SDaniele Ceraolo Spurio 	if (ver_->build) \
6402e7227b4SDaniele Ceraolo Spurio 		drm_printf(p_, prefix_ " version %u.%u.%u.%u\n", ##__VA_ARGS__, \
6412e7227b4SDaniele Ceraolo Spurio 			   ver_->major, ver_->minor, \
6422e7227b4SDaniele Ceraolo Spurio 			   ver_->patch, ver_->build); \
6432e7227b4SDaniele Ceraolo Spurio 	else \
6442e7227b4SDaniele Ceraolo Spurio 		drm_printf(p_, prefix_ " version %u.%u.%u\n", ##__VA_ARGS__, \
6452e7227b4SDaniele Ceraolo Spurio 			  ver_->major, ver_->minor, ver_->patch); \
6462e7227b4SDaniele Ceraolo Spurio } while (0)
6472e7227b4SDaniele Ceraolo Spurio 
648c93ea051SMichał Winiarski static int uc_fw_request(struct xe_uc_fw *uc_fw, const struct firmware **firmware_p)
649dd08ebf6SMatthew Brost {
650dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
651dd08ebf6SMatthew Brost 	struct device *dev = xe->drm.dev;
6522e7227b4SDaniele Ceraolo Spurio 	struct drm_printer p = drm_info_printer(dev);
653dd08ebf6SMatthew Brost 	const struct firmware *fw = NULL;
654dd08ebf6SMatthew Brost 	int err;
655dd08ebf6SMatthew Brost 
656dd08ebf6SMatthew Brost 	/*
657dd08ebf6SMatthew Brost 	 * we use FIRMWARE_UNINITIALIZED to detect checks against uc_fw->status
658dd08ebf6SMatthew Brost 	 * before we're looked at the HW caps to see if we have uc support
659dd08ebf6SMatthew Brost 	 */
660dd08ebf6SMatthew Brost 	BUILD_BUG_ON(XE_UC_FIRMWARE_UNINITIALIZED);
661c73acc1eSFrancois Dugast 	xe_assert(xe, !uc_fw->status);
662c73acc1eSFrancois Dugast 	xe_assert(xe, !uc_fw->path);
663dd08ebf6SMatthew Brost 
664dd08ebf6SMatthew Brost 	uc_fw_auto_select(xe, uc_fw);
66566cb3ca9SMichal Wajdeczko 
66666cb3ca9SMichal Wajdeczko 	if (IS_SRIOV_VF(xe)) {
66705e49e0cSMichal Wajdeczko 		/* Only GuC/HuC are supported */
66805e49e0cSMichal Wajdeczko 		if (uc_fw->type != XE_UC_FW_TYPE_GUC &&
66905e49e0cSMichal Wajdeczko 		    uc_fw->type != XE_UC_FW_TYPE_HUC)
67005e49e0cSMichal Wajdeczko 			uc_fw->path = NULL;
67166cb3ca9SMichal Wajdeczko 		/* VF will support only firmwares that driver can autoselect */
67266cb3ca9SMichal Wajdeczko 		xe_uc_fw_change_status(uc_fw, uc_fw->path ?
67366cb3ca9SMichal Wajdeczko 				       XE_UC_FIRMWARE_PRELOADED :
67466cb3ca9SMichal Wajdeczko 				       XE_UC_FIRMWARE_NOT_SUPPORTED);
67566cb3ca9SMichal Wajdeczko 		return 0;
67666cb3ca9SMichal Wajdeczko 	}
67766cb3ca9SMichal Wajdeczko 
67845883418SLucas De Marchi 	uc_fw_override(uc_fw);
67966cb3ca9SMichal Wajdeczko 
68075730847SDaniele Ceraolo Spurio 	xe_uc_fw_change_status(uc_fw, uc_fw->path ?
681dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_SELECTED :
682dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_NOT_SUPPORTED);
683dd08ebf6SMatthew Brost 
6846badfc46SLucas De Marchi 	if (!xe_uc_fw_is_supported(uc_fw)) {
6856badfc46SLucas De Marchi 		if (uc_fw->type == XE_UC_FW_TYPE_GUC) {
6866badfc46SLucas De Marchi 			drm_err(&xe->drm, "No GuC firmware defined for platform\n");
6876badfc46SLucas De Marchi 			return -ENOENT;
6886badfc46SLucas De Marchi 		}
68975730847SDaniele Ceraolo Spurio 		return 0;
6906badfc46SLucas De Marchi 	}
69175730847SDaniele Ceraolo Spurio 
692a455ed04SDaniele Ceraolo Spurio 	/* an empty path means the firmware is disabled */
693a455ed04SDaniele Ceraolo Spurio 	if (!xe_device_uc_enabled(xe) || !(*uc_fw->path)) {
694dd08ebf6SMatthew Brost 		xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_DISABLED);
69575730847SDaniele Ceraolo Spurio 		drm_dbg(&xe->drm, "%s disabled", xe_uc_fw_type_repr(uc_fw->type));
69675730847SDaniele Ceraolo Spurio 		return 0;
697dd08ebf6SMatthew Brost 	}
69875730847SDaniele Ceraolo Spurio 
699dd08ebf6SMatthew Brost 	err = request_firmware(&fw, uc_fw->path, dev);
700dd08ebf6SMatthew Brost 	if (err)
701dd08ebf6SMatthew Brost 		goto fail;
702dd08ebf6SMatthew Brost 
703a9a95523SDaniele Ceraolo Spurio 	err = parse_headers(uc_fw, fw);
704a9a95523SDaniele Ceraolo Spurio 	if (err)
705dd08ebf6SMatthew Brost 		goto fail;
706dd08ebf6SMatthew Brost 
7072e7227b4SDaniele Ceraolo Spurio 	print_uc_fw_version(&p,
7082e7227b4SDaniele Ceraolo Spurio 			    &uc_fw->versions.found[XE_UC_FW_VER_RELEASE],
7092e7227b4SDaniele Ceraolo Spurio 			    "Using %s firmware from %s",
7102e7227b4SDaniele Ceraolo Spurio 			    xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
71161e72e77SLucas De Marchi 
7120881cbe0SDaniele Ceraolo Spurio 	/* for GSC FW we want the compatibility version, which we query after load */
7130881cbe0SDaniele Ceraolo Spurio 	if (uc_fw->type != XE_UC_FW_TYPE_GSC) {
7140881cbe0SDaniele Ceraolo Spurio 		err = xe_uc_fw_check_version_requirements(uc_fw);
715ad55ead7SLucas De Marchi 		if (err)
716dd08ebf6SMatthew Brost 			goto fail;
7170881cbe0SDaniele Ceraolo Spurio 	}
718dd08ebf6SMatthew Brost 
719c93ea051SMichał Winiarski 	*firmware_p = fw;
720dd08ebf6SMatthew Brost 
721dd08ebf6SMatthew Brost 	return 0;
722dd08ebf6SMatthew Brost 
723dd08ebf6SMatthew Brost fail:
724dd08ebf6SMatthew Brost 	xe_uc_fw_change_status(uc_fw, err == -ENOENT ?
725dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_MISSING :
726dd08ebf6SMatthew Brost 			       XE_UC_FIRMWARE_ERROR);
727dd08ebf6SMatthew Brost 
728dd08ebf6SMatthew Brost 	drm_notice(&xe->drm, "%s firmware %s: fetch failed with error %d\n",
729dd08ebf6SMatthew Brost 		   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
730dd08ebf6SMatthew Brost 	drm_info(&xe->drm, "%s firmware(s) can be downloaded from %s\n",
731dd08ebf6SMatthew Brost 		 xe_uc_fw_type_repr(uc_fw->type), XE_UC_FIRMWARE_URL);
732dd08ebf6SMatthew Brost 
733dd08ebf6SMatthew Brost 	release_firmware(fw);		/* OK even if fw is NULL */
7340e1a47fcSMichał Winiarski 
735dd08ebf6SMatthew Brost 	return err;
736dd08ebf6SMatthew Brost }
737dd08ebf6SMatthew Brost 
738c93ea051SMichał Winiarski static void uc_fw_release(const struct firmware *fw)
739c93ea051SMichał Winiarski {
740c93ea051SMichał Winiarski 	release_firmware(fw);
741c93ea051SMichał Winiarski }
742c93ea051SMichał Winiarski 
743c93ea051SMichał Winiarski static int uc_fw_copy(struct xe_uc_fw *uc_fw, const void *data, size_t size, u32 flags)
744c93ea051SMichał Winiarski {
745c93ea051SMichał Winiarski 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
746c93ea051SMichał Winiarski 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
747c93ea051SMichał Winiarski 	struct xe_tile *tile = gt_to_tile(gt);
748c93ea051SMichał Winiarski 	struct xe_bo *obj;
749c93ea051SMichał Winiarski 	int err;
750c93ea051SMichał Winiarski 
751c93ea051SMichał Winiarski 	obj = xe_managed_bo_create_from_data(xe, tile, data, size, flags);
752c93ea051SMichał Winiarski 	if (IS_ERR(obj)) {
753c93ea051SMichał Winiarski 		drm_notice(&xe->drm, "%s firmware %s: failed to create / populate bo",
754c93ea051SMichał Winiarski 			   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
755c93ea051SMichał Winiarski 		err = PTR_ERR(obj);
756c93ea051SMichał Winiarski 		goto fail;
757c93ea051SMichał Winiarski 	}
758c93ea051SMichał Winiarski 
759c93ea051SMichał Winiarski 	uc_fw->bo = obj;
760c93ea051SMichał Winiarski 	uc_fw->size = size;
761c93ea051SMichał Winiarski 
762c93ea051SMichał Winiarski 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_AVAILABLE);
763c93ea051SMichał Winiarski 
764c93ea051SMichał Winiarski 	err = drmm_add_action_or_reset(&xe->drm, uc_fw_fini, uc_fw);
765c93ea051SMichał Winiarski 	if (err)
766c93ea051SMichał Winiarski 		goto fail;
767c93ea051SMichał Winiarski 
768c93ea051SMichał Winiarski 	return 0;
769c93ea051SMichał Winiarski 
770c93ea051SMichał Winiarski fail:
771c93ea051SMichał Winiarski 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_ERROR);
772c93ea051SMichał Winiarski 	drm_notice(&xe->drm, "%s firmware %s: copy failed with error %d\n",
773c93ea051SMichał Winiarski 		   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
774c93ea051SMichał Winiarski 
775c93ea051SMichał Winiarski 	return err;
776c93ea051SMichał Winiarski }
777c93ea051SMichał Winiarski 
778c93ea051SMichał Winiarski int xe_uc_fw_init(struct xe_uc_fw *uc_fw)
779c93ea051SMichał Winiarski {
780c93ea051SMichał Winiarski 	const struct firmware *fw = NULL;
781c93ea051SMichał Winiarski 	int err;
782c93ea051SMichał Winiarski 
783c93ea051SMichał Winiarski 	err = uc_fw_request(uc_fw, &fw);
784c93ea051SMichał Winiarski 	if (err)
785c93ea051SMichał Winiarski 		return err;
786c93ea051SMichał Winiarski 
787c93ea051SMichał Winiarski 	/* no error and no firmware means nothing to copy */
788c93ea051SMichał Winiarski 	if (!fw)
789c93ea051SMichał Winiarski 		return 0;
790c93ea051SMichał Winiarski 
791c93ea051SMichał Winiarski 	err = uc_fw_copy(uc_fw, fw->data, fw->size,
79262742d12SLucas De Marchi 			 XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT |
79362742d12SLucas De Marchi 			 XE_BO_FLAG_GGTT_INVALIDATE);
794c93ea051SMichał Winiarski 
795c93ea051SMichał Winiarski 	uc_fw_release(fw);
796c93ea051SMichał Winiarski 
797c93ea051SMichał Winiarski 	return err;
798c93ea051SMichał Winiarski }
799c93ea051SMichał Winiarski 
800dd08ebf6SMatthew Brost static u32 uc_fw_ggtt_offset(struct xe_uc_fw *uc_fw)
801dd08ebf6SMatthew Brost {
802dd08ebf6SMatthew Brost 	return xe_bo_ggtt_addr(uc_fw->bo);
803dd08ebf6SMatthew Brost }
804dd08ebf6SMatthew Brost 
805dd08ebf6SMatthew Brost static int uc_fw_xfer(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags)
806dd08ebf6SMatthew Brost {
807dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
808dd08ebf6SMatthew Brost 	struct xe_gt *gt = uc_fw_to_gt(uc_fw);
8094c15a6dcSDaniele Ceraolo Spurio 	u64 src_offset;
8104c15a6dcSDaniele Ceraolo Spurio 	u32 dma_ctrl;
811dd08ebf6SMatthew Brost 	int ret;
812dd08ebf6SMatthew Brost 
813dd08ebf6SMatthew Brost 	xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
814dd08ebf6SMatthew Brost 
815dd08ebf6SMatthew Brost 	/* Set the source address for the uCode */
816484ecffaSDaniele Ceraolo Spurio 	src_offset = uc_fw_ggtt_offset(uc_fw) + uc_fw->css_offset;
817ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_ADDR_0_LOW, lower_32_bits(src_offset));
818473b6276SFei Yang 	xe_mmio_write32(gt, DMA_ADDR_0_HIGH,
819473b6276SFei Yang 			upper_32_bits(src_offset) | DMA_ADDRESS_SPACE_GGTT);
820dd08ebf6SMatthew Brost 
821dd08ebf6SMatthew Brost 	/* Set the DMA destination */
822ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_ADDR_1_LOW, offset);
823ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);
824dd08ebf6SMatthew Brost 
825dd08ebf6SMatthew Brost 	/*
826dd08ebf6SMatthew Brost 	 * Set the transfer size. The header plus uCode will be copied to WOPCM
827dd08ebf6SMatthew Brost 	 * via DMA, excluding any other components
828dd08ebf6SMatthew Brost 	 */
829ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_COPY_SIZE,
830dd08ebf6SMatthew Brost 			sizeof(struct uc_css_header) + uc_fw->ucode_size);
831dd08ebf6SMatthew Brost 
832dd08ebf6SMatthew Brost 	/* Start the DMA */
833ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_CTRL,
834dd08ebf6SMatthew Brost 			_MASKED_BIT_ENABLE(dma_flags | START_DMA));
835dd08ebf6SMatthew Brost 
836dd08ebf6SMatthew Brost 	/* Wait for DMA to finish */
837063e09afSRodrigo Vivi 	ret = xe_mmio_wait32(gt, DMA_CTRL, START_DMA, 0, 100000, &dma_ctrl,
8387dc9b92dSRodrigo Vivi 			     false);
839dd08ebf6SMatthew Brost 	if (ret)
840dd08ebf6SMatthew Brost 		drm_err(&xe->drm, "DMA for %s fw failed, DMA_CTRL=%u\n",
8417aaec3a6SRodrigo Vivi 			xe_uc_fw_type_repr(uc_fw->type), dma_ctrl);
842dd08ebf6SMatthew Brost 
843dd08ebf6SMatthew Brost 	/* Disable the bits once DMA is over */
844ce8bf5bdSLucas De Marchi 	xe_mmio_write32(gt, DMA_CTRL, _MASKED_BIT_DISABLE(dma_flags));
845dd08ebf6SMatthew Brost 
846dd08ebf6SMatthew Brost 	return ret;
847dd08ebf6SMatthew Brost }
848dd08ebf6SMatthew Brost 
849dd08ebf6SMatthew Brost int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags)
850dd08ebf6SMatthew Brost {
851dd08ebf6SMatthew Brost 	struct xe_device *xe = uc_fw_to_xe(uc_fw);
852dd08ebf6SMatthew Brost 	int err;
853dd08ebf6SMatthew Brost 
854dd08ebf6SMatthew Brost 	/* make sure the status was cleared the last time we reset the uc */
855c73acc1eSFrancois Dugast 	xe_assert(xe, !xe_uc_fw_is_loaded(uc_fw));
856dd08ebf6SMatthew Brost 
857dd08ebf6SMatthew Brost 	if (!xe_uc_fw_is_loadable(uc_fw))
858dd08ebf6SMatthew Brost 		return -ENOEXEC;
859dd08ebf6SMatthew Brost 
860dd08ebf6SMatthew Brost 	/* Call custom loader */
861dd08ebf6SMatthew Brost 	err = uc_fw_xfer(uc_fw, offset, dma_flags);
862dd08ebf6SMatthew Brost 	if (err)
863dd08ebf6SMatthew Brost 		goto fail;
864dd08ebf6SMatthew Brost 
865dd08ebf6SMatthew Brost 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_TRANSFERRED);
866dd08ebf6SMatthew Brost 	return 0;
867dd08ebf6SMatthew Brost 
868dd08ebf6SMatthew Brost fail:
869dd08ebf6SMatthew Brost 	drm_err(&xe->drm, "Failed to load %s firmware %s (%d)\n",
870dd08ebf6SMatthew Brost 		xe_uc_fw_type_repr(uc_fw->type), uc_fw->path,
871dd08ebf6SMatthew Brost 		err);
872dd08ebf6SMatthew Brost 	xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_LOAD_FAIL);
873dd08ebf6SMatthew Brost 	return err;
874dd08ebf6SMatthew Brost }
875dd08ebf6SMatthew Brost 
8762e7227b4SDaniele Ceraolo Spurio static const char *version_type_repr(enum xe_uc_fw_version_types type)
8772e7227b4SDaniele Ceraolo Spurio {
8782e7227b4SDaniele Ceraolo Spurio 	switch (type) {
8792e7227b4SDaniele Ceraolo Spurio 	case XE_UC_FW_VER_RELEASE:
8802e7227b4SDaniele Ceraolo Spurio 		return "release";
8812e7227b4SDaniele Ceraolo Spurio 	case XE_UC_FW_VER_COMPATIBILITY:
8822e7227b4SDaniele Ceraolo Spurio 		return "compatibility";
8832e7227b4SDaniele Ceraolo Spurio 	default:
8842e7227b4SDaniele Ceraolo Spurio 		return "Unknown version type";
8852e7227b4SDaniele Ceraolo Spurio 	}
8862e7227b4SDaniele Ceraolo Spurio }
887dd08ebf6SMatthew Brost 
888dd08ebf6SMatthew Brost void xe_uc_fw_print(struct xe_uc_fw *uc_fw, struct drm_printer *p)
889dd08ebf6SMatthew Brost {
8902e7227b4SDaniele Ceraolo Spurio 	int i;
8912e7227b4SDaniele Ceraolo Spurio 
892dd08ebf6SMatthew Brost 	drm_printf(p, "%s firmware: %s\n",
893dd08ebf6SMatthew Brost 		   xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
894dd08ebf6SMatthew Brost 	drm_printf(p, "\tstatus: %s\n",
895dd08ebf6SMatthew Brost 		   xe_uc_fw_status_repr(uc_fw->status));
89699c821b0SMatthew Brost 
8972e7227b4SDaniele Ceraolo Spurio 	print_uc_fw_version(p, &uc_fw->versions.wanted, "\twanted %s",
8982e7227b4SDaniele Ceraolo Spurio 			    version_type_repr(uc_fw->versions.wanted_type));
89999c821b0SMatthew Brost 
9002e7227b4SDaniele Ceraolo Spurio 	for (i = 0; i < XE_UC_FW_VER_TYPE_COUNT; i++) {
9012e7227b4SDaniele Ceraolo Spurio 		struct xe_uc_fw_version *ver = &uc_fw->versions.found[i];
9022e7227b4SDaniele Ceraolo Spurio 
9032e7227b4SDaniele Ceraolo Spurio 		if (ver->major)
9042e7227b4SDaniele Ceraolo Spurio 			print_uc_fw_version(p, ver, "\tfound %s",
9052e7227b4SDaniele Ceraolo Spurio 					    version_type_repr(i));
90699c821b0SMatthew Brost 	}
9072e7227b4SDaniele Ceraolo Spurio 
9082e7227b4SDaniele Ceraolo Spurio 	if (uc_fw->ucode_size)
9092e7227b4SDaniele Ceraolo Spurio 		drm_printf(p, "\tuCode: %u bytes\n", uc_fw->ucode_size);
9102e7227b4SDaniele Ceraolo Spurio 	if (uc_fw->rsa_size)
9112e7227b4SDaniele Ceraolo Spurio 		drm_printf(p, "\tRSA: %u bytes\n", uc_fw->rsa_size);
912dd08ebf6SMatthew Brost }
913