xref: /linux/drivers/gpu/drm/xe/xe_uc_fw.c (revision 2e7227b4b733223a0a5b6a7a2685c7ff089c21c5)
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